@demlik/tea 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/agent/index.d.ts +16 -770
- package/dist/agent/index.js +18 -16
- package/dist/authed-call/index.d.ts +3 -3
- package/dist/authed-call/index.js +22 -36
- package/dist/authed-call/index.js.map +1 -1
- package/dist/await-terminal/index.d.ts +2 -2
- package/dist/await-terminal/index.js +2 -2
- package/dist/batch-window/index.d.ts +3 -3
- package/dist/batch-window/index.js +3 -3
- package/dist/cache/index.d.ts +2 -2
- package/dist/cache/index.js +5 -2
- package/dist/{chunk-3545BOYL.js → chunk-25LBDCBC.js} +6 -5
- package/dist/chunk-25LBDCBC.js.map +1 -0
- package/dist/{chunk-IN33Q4VJ.js → chunk-2KNLKQVN.js} +3 -3
- package/dist/{chunk-IN33Q4VJ.js.map → chunk-2KNLKQVN.js.map} +1 -1
- package/dist/{chunk-MMH7CW5K.js → chunk-BP2R7TPP.js} +216 -157
- package/dist/chunk-BP2R7TPP.js.map +1 -0
- package/dist/chunk-C6QC273X.js +71 -0
- package/dist/chunk-C6QC273X.js.map +1 -0
- package/dist/{chunk-7W4LMPUW.js → chunk-CAXMQJJ3.js} +17 -24
- package/dist/chunk-CAXMQJJ3.js.map +1 -0
- package/dist/{chunk-UV6UWGEO.js → chunk-D736QUGF.js} +6 -5
- package/dist/chunk-D736QUGF.js.map +1 -0
- package/dist/chunk-EWJP3YYP.js +21 -0
- package/dist/chunk-EWJP3YYP.js.map +1 -0
- package/dist/{chunk-TGG26XMK.js → chunk-FV3Z5MBE.js} +10 -5
- package/dist/chunk-FV3Z5MBE.js.map +1 -0
- package/dist/chunk-G5E74IJB.js +8 -0
- package/dist/chunk-G5E74IJB.js.map +1 -0
- package/dist/{chunk-27N2SPTN.js → chunk-IXBKFQH5.js} +12 -11
- package/dist/chunk-IXBKFQH5.js.map +1 -0
- package/dist/{chunk-SQTCS44W.js → chunk-JFFVBEDI.js} +3 -3
- package/dist/{chunk-SQTCS44W.js.map → chunk-JFFVBEDI.js.map} +1 -1
- package/dist/chunk-LYLDCIPH.js +365 -0
- package/dist/chunk-LYLDCIPH.js.map +1 -0
- package/dist/{chunk-HJO6VNQI.js → chunk-MU6MCIAC.js} +3 -3
- package/dist/{chunk-HJO6VNQI.js.map → chunk-MU6MCIAC.js.map} +1 -1
- package/dist/{chunk-UYWUVEP6.js → chunk-MUUROGIH.js} +2 -2
- package/dist/chunk-MUUROGIH.js.map +1 -0
- package/dist/{chunk-EHIX7NYZ.js → chunk-MVY5DAMF.js} +4 -4
- package/dist/{chunk-EHIX7NYZ.js.map → chunk-MVY5DAMF.js.map} +1 -1
- package/dist/{chunk-LP7VQBRG.js → chunk-NPERUUUE.js} +76 -95
- package/dist/chunk-NPERUUUE.js.map +1 -0
- package/dist/{chunk-QLA76GIT.js → chunk-PBDYW2TU.js} +68 -9
- package/dist/chunk-PBDYW2TU.js.map +1 -0
- package/dist/{chunk-5VNWZADP.js → chunk-Q2UM7BXU.js} +18 -7
- package/dist/chunk-Q2UM7BXU.js.map +1 -0
- package/dist/chunk-RMX4EOQK.js +26 -0
- package/dist/chunk-RMX4EOQK.js.map +1 -0
- package/dist/{chunk-2YOE5PIS.js → chunk-S2EGRPCM.js} +8 -6
- package/dist/chunk-S2EGRPCM.js.map +1 -0
- package/dist/{chunk-ON24RKSE.js → chunk-S4O26D7B.js} +7 -14
- package/dist/chunk-S4O26D7B.js.map +1 -0
- package/dist/{chunk-3XJWC7VW.js → chunk-SK5JGUY7.js} +3 -3
- package/dist/{chunk-3XJWC7VW.js.map → chunk-SK5JGUY7.js.map} +1 -1
- package/dist/{chunk-5DEQ5I3U.js → chunk-TPGASTFF.js} +8 -10
- package/dist/chunk-TPGASTFF.js.map +1 -0
- package/dist/{chunk-SC3FAFZN.js → chunk-UF4MZSLT.js} +2 -2
- package/dist/chunk-UF4MZSLT.js.map +1 -0
- package/dist/{chunk-PS23H6KQ.js → chunk-VBUCHVCV.js} +6 -5
- package/dist/chunk-VBUCHVCV.js.map +1 -0
- package/dist/{chunk-S3FQN4SL.js → chunk-WGGAMW7Y.js} +191 -178
- package/dist/chunk-WGGAMW7Y.js.map +1 -0
- package/dist/chunk-ZJ5AOYBH.js +8 -0
- package/dist/chunk-ZJ5AOYBH.js.map +1 -0
- package/dist/{chunk-LEC4U6CX.js → chunk-ZVJZ72HY.js} +3 -3
- package/dist/{chunk-LEC4U6CX.js.map → chunk-ZVJZ72HY.js.map} +1 -1
- package/dist/circuit-breaker/index.d.ts +1 -0
- package/dist/circuit-breaker/index.js +1 -1
- package/dist/{core-BoB6t7dB.d.ts → core-42kapW0n.d.ts} +105 -6
- package/dist/deadline/index.d.ts +77 -11
- package/dist/deadline/index.js +3 -3
- package/dist/devtools/index.js +8 -28
- package/dist/devtools/index.js.map +1 -1
- package/dist/do/index.d.ts +483 -339
- package/dist/do/index.js +244 -214
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-CO9Uh971.d.ts → durable-effects-CFRngTNA.d.ts} +3 -3
- package/dist/extension/index.d.ts +25 -7
- package/dist/extension/index.js +4 -3
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/react.js +4 -3
- package/dist/extension/react.js.map +1 -1
- package/dist/extension/subs/index.d.ts +2 -2
- package/dist/extension/subs/index.js +7 -10
- package/dist/extension/subs/index.js.map +1 -1
- package/dist/fan-out/index.d.ts +10 -10
- package/dist/fan-out/index.js +3 -3
- package/dist/idempotent-intake/index.d.ts +2 -2
- package/dist/idempotent-intake/index.js +3 -3
- package/dist/index.d.ts +50 -472
- package/dist/index.js +2 -2
- package/dist/llm-call/index.d.ts +3 -2
- package/dist/llm-call/index.js +12 -10
- package/dist/machine-B_BF4jij.d.ts +804 -0
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +3 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/monitored-run/index.d.ts +54 -30
- package/dist/monitored-run/index.js +7 -7
- package/dist/node/index.d.ts +3 -2
- package/dist/node/index.js +2 -3
- package/dist/node/index.js.map +1 -1
- package/dist/paginated-walk/index.d.ts +2 -2
- package/dist/paginated-walk/index.js +11 -10
- package/dist/parity/index.d.ts +6 -5
- package/dist/parity/index.js +5 -4
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/arbitraries/index.d.ts +1 -1
- package/dist/pbt/index.d.ts +1 -1
- package/dist/pbt/index.js +3 -3
- package/dist/pbt/index.js.map +1 -1
- package/dist/pbt/runners/index.d.ts +1 -1
- package/dist/pbt/runners/index.js +3 -3
- package/dist/poller/index.d.ts +59 -17
- package/dist/poller/index.js +56 -20
- package/dist/poller/index.js.map +1 -1
- package/dist/prediction/index.d.ts +1 -1
- package/dist/prediction/index.js +2 -2
- package/dist/pure/index.d.ts +1 -1
- package/dist/pure/index.js +2 -2
- package/dist/rate-limit/index.d.ts +2 -2
- package/dist/rate-limit/index.js +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +2 -2
- package/dist/react/index.js.map +1 -1
- package/dist/reconciler/index.d.ts +2 -2
- package/dist/reconciler/index.js +13 -15
- package/dist/reconciler/index.js.map +1 -1
- package/dist/recorder/index.d.ts +20 -20
- package/dist/recorder/index.js +1 -1
- package/dist/resilient-call/index.d.ts +31 -6
- package/dist/resilient-call/index.js +10 -9
- package/dist/retry-backoff/index.d.ts +191 -14
- package/dist/retry-backoff/index.js +1 -1
- package/dist/retry-to-success/index.js +1 -1
- package/dist/runtime-types-gsog27b6.d.ts +337 -0
- package/dist/saga/index.d.ts +3 -2
- package/dist/saga/index.js +2 -2
- package/dist/saga/index.js.map +1 -1
- package/dist/snapshot/index.d.ts +1 -1
- package/dist/snapshot/index.js +3 -3
- package/dist/subs/index.d.ts +372 -4
- package/dist/subs/index.js +4 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/throttled-input/index.d.ts +2 -2
- package/dist/throttled-input/index.js +14 -13
- package/dist/throttled-input/index.js.map +1 -1
- package/dist/token-refresh/index.d.ts +1 -1
- package/dist/token-refresh/index.js +3 -3
- package/dist/trace-replay/index.d.ts +12 -7
- package/dist/trace-replay/index.js +4 -3
- package/dist/{types-B-8bcAp8.d.ts → types-KbL3W1mj.d.ts} +1 -1
- package/dist/with-deadline/index.d.ts +1 -1
- package/dist/with-deadline/index.js +2 -2
- package/dist/with-resilience/index.d.ts +21 -2
- package/dist/with-resilience/index.js +12 -23
- package/dist/with-resilience/index.js.map +1 -1
- package/dist/with-telemetry/index.d.ts +1 -1
- package/dist/with-telemetry/index.js +2 -2
- package/dist/work-queue/adapter.d.ts +2 -2
- package/dist/work-queue/adapter.js +2 -2
- package/dist/work-queue/index.d.ts +3 -2
- package/dist/work-queue/index.js +1 -1
- package/dist/work-queue/index.js.map +1 -1
- package/dist/work-queue/ops.d.ts +2 -2
- package/dist/work-queue/ops.js +1 -1
- package/dist/workflow/index.d.ts +51 -19
- package/dist/workflow/index.js +71 -52
- package/dist/workflow/index.js.map +1 -1
- package/package.json +4 -19
- package/dist/chunk-27N2SPTN.js.map +0 -1
- package/dist/chunk-2YOE5PIS.js.map +0 -1
- package/dist/chunk-3545BOYL.js.map +0 -1
- package/dist/chunk-5DEQ5I3U.js.map +0 -1
- package/dist/chunk-5VNWZADP.js.map +0 -1
- package/dist/chunk-6RGBKODV.js +0 -268
- package/dist/chunk-6RGBKODV.js.map +0 -1
- package/dist/chunk-7W4LMPUW.js.map +0 -1
- package/dist/chunk-DILMKOZ7.js +0 -91
- package/dist/chunk-DILMKOZ7.js.map +0 -1
- package/dist/chunk-K7Q23EQ4.js +0 -20
- package/dist/chunk-K7Q23EQ4.js.map +0 -1
- package/dist/chunk-LP7VQBRG.js.map +0 -1
- package/dist/chunk-MMH7CW5K.js.map +0 -1
- package/dist/chunk-NSDFA6M6.js +0 -198
- package/dist/chunk-NSDFA6M6.js.map +0 -1
- package/dist/chunk-ON24RKSE.js.map +0 -1
- package/dist/chunk-PS23H6KQ.js.map +0 -1
- package/dist/chunk-QLA76GIT.js.map +0 -1
- package/dist/chunk-S3FQN4SL.js.map +0 -1
- package/dist/chunk-SC3FAFZN.js.map +0 -1
- package/dist/chunk-TGG26XMK.js.map +0 -1
- package/dist/chunk-UV6UWGEO.js.map +0 -1
- package/dist/chunk-UYWUVEP6.js.map +0 -1
- package/dist/chunk-VLGARN6H.js +0 -202
- package/dist/chunk-VLGARN6H.js.map +0 -1
- package/dist/raft/demo.d.ts +0 -105
- package/dist/raft/demo.js +0 -172
- package/dist/raft/demo.js.map +0 -1
- package/dist/raft/index.d.ts +0 -548
- package/dist/raft/index.js +0 -7
- package/dist/raft/index.js.map +0 -1
- package/dist/raft/sim.d.ts +0 -222
- package/dist/raft/sim.js +0 -8
- package/dist/raft/sim.js.map +0 -1
- package/dist/raft/timeline.d.ts +0 -46
- package/dist/raft/timeline.js +0 -62
- package/dist/raft/timeline.js.map +0 -1
package/dist/do/index.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { Store, Runtime, BootingRuntime } from '../
|
|
2
|
-
import { P as PendingEffectsRecorder, b as EffectOwed, a as EffectConfirmed } from '../durable-effects-
|
|
3
|
-
export { D as DeliveryId, E as EffectLedgerEvent, O as OwedEffect, d as PendingEffectsLedger, c as applyEffectEvent, e as emptyLedger, f as foldLedger, i as isOwed, p as pendingEffectsLedger, s as survivingEffects } from '../durable-effects-
|
|
1
|
+
import { S as Store, R as Runtime, B as BootingRuntime } from '../runtime-types-gsog27b6.js';
|
|
2
|
+
import { P as PendingEffectsRecorder, b as EffectOwed, a as EffectConfirmed } from '../durable-effects-CFRngTNA.js';
|
|
3
|
+
export { D as DeliveryId, E as EffectLedgerEvent, O as OwedEffect, d as PendingEffectsLedger, c as applyEffectEvent, e as emptyLedger, f as foldLedger, i as isOwed, p as pendingEffectsLedger, s as survivingEffects } from '../durable-effects-CFRngTNA.js';
|
|
4
4
|
import { RetryPolicy, Rng } from '../retry-backoff/index.js';
|
|
5
|
-
import { C as Cmd, S as Sub, M as Machine } from '../core-
|
|
6
|
-
import { AgentTurn, AgentState, AgentMachineMsg,
|
|
7
|
-
import '../fan-out/index.js';
|
|
8
|
-
import '../work-queue/index.js';
|
|
5
|
+
import { C as Cmd, S as Sub, M as Machine } from '../core-42kapW0n.js';
|
|
6
|
+
import { y as AgentEvent, A as AgentTurn, N as AgentState, x as AgentMachineMsg, P as AgentStatus } from '../machine-B_BF4jij.js';
|
|
9
7
|
import '../llm-call/index.js';
|
|
10
8
|
import '../resilient-call/index.js';
|
|
11
9
|
import '../cache/index.js';
|
|
12
|
-
import '../types-
|
|
10
|
+
import '../types-KbL3W1mj.js';
|
|
13
11
|
import '../circuit-breaker/index.js';
|
|
14
12
|
import '../deadline/index.js';
|
|
15
13
|
import '../protocol-B5YmJr0d.js';
|
|
16
14
|
import '../rate-limit/index.js';
|
|
17
15
|
import '../monitored-run/index.js';
|
|
18
16
|
import '../snapshot/index.js';
|
|
17
|
+
import '../work-queue/index.js';
|
|
18
|
+
import '../fan-out/index.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @demlik/tea/do — `stepHost`, the HTTP-PULL control carrier (#92).
|
|
@@ -117,6 +117,13 @@ interface NextStep<I> {
|
|
|
117
117
|
* A `/step` response — a discriminated union on `done`:
|
|
118
118
|
* - `{ done: false, step }` — execute `step`, POST its result, ask again.
|
|
119
119
|
* - `{ done: true, output }` — the run is terminal; `output` is its result.
|
|
120
|
+
*
|
|
121
|
+
* This is the ORIGINAL 2-arm INLINE contract, kept byte-identical: a host that
|
|
122
|
+
* resolves each step inside the held request returns exactly these two shapes.
|
|
123
|
+
* The not-ready arm ({@link StepWorking}) is a SEPARATE, opt-in type reachable
|
|
124
|
+
* only through the defer-resume hook — see {@link DeferredStepResponse}. Keeping
|
|
125
|
+
* `StepResponse` a strict subset is what makes inline adopters unaffected: their
|
|
126
|
+
* `if (r.done) … else r.step` narrowing never sees a third member.
|
|
120
127
|
*/
|
|
121
128
|
type StepResponse<I, O> = {
|
|
122
129
|
readonly done: false;
|
|
@@ -125,6 +132,37 @@ type StepResponse<I, O> = {
|
|
|
125
132
|
readonly done: true;
|
|
126
133
|
readonly output: O;
|
|
127
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* The NOT-READY arm — the run is computing OUT-OF-BAND under a defer-resume host
|
|
137
|
+
* (Binclusive ADR 0035 / prod incident #1873: a non-blocking pull carrier must
|
|
138
|
+
* answer a pull with an explicit "computing, poll again" instead of holding the
|
|
139
|
+
* request across a multi-second step). The held `/step` request returns this
|
|
140
|
+
* IMMEDIATELY instead of inline-awaiting `engine.resume`; the hands re-poll
|
|
141
|
+
* (optionally after `retryAfterMs`) until the deferred compute lands in the
|
|
142
|
+
* durable checkpoint and a real {@link StepResponse} step/done arm is returned.
|
|
143
|
+
*
|
|
144
|
+
* It is a first-class discriminated member (`working: true`), NOT a hollow
|
|
145
|
+
* `{ done: false }` with an absent/stale `step` — the not-ready state is made
|
|
146
|
+
* representable so it can never be confused with a real pending step.
|
|
147
|
+
*/
|
|
148
|
+
interface StepWorking {
|
|
149
|
+
readonly done: false;
|
|
150
|
+
readonly working: true;
|
|
151
|
+
/**
|
|
152
|
+
* Advisory earliest re-poll delay as a hint to the hands (ms to wait before
|
|
153
|
+
* the next pull). Purely advisory — a host may omit it and the hands fall back
|
|
154
|
+
* to their own backoff curve.
|
|
155
|
+
*/
|
|
156
|
+
readonly retryAfterMs?: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The 3-arm response a DEFER-RESUME host returns — the inline {@link StepResponse}
|
|
160
|
+
* arms PLUS {@link StepWorking}. A superset of `StepResponse`, so a 2-arm value is
|
|
161
|
+
* assignable to it (the widening is safe for existing transports); discriminate
|
|
162
|
+
* with `if (r.done)` for terminal, then `"working" in r` for the not-ready arm,
|
|
163
|
+
* else the step arm.
|
|
164
|
+
*/
|
|
165
|
+
type DeferredStepResponse<I, O> = StepResponse<I, O> | StepWorking;
|
|
128
166
|
/**
|
|
129
167
|
* Mint a fresh per-run capability token: 32 random bytes, hex-encoded. Uses the
|
|
130
168
|
* Web Crypto `crypto.getRandomValues` available in the Workers runtime (and in
|
|
@@ -202,6 +240,51 @@ interface StepHostConfig {
|
|
|
202
240
|
*/
|
|
203
241
|
readonly giveUpAfterMs?: number;
|
|
204
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* The DEFER-RESUME hook — the opt-in seam that drives `engine.resume` OUT of the
|
|
245
|
+
* held `/step` request. A non-blocking host (Binclusive audit carrier, ADR 0035)
|
|
246
|
+
* cannot inline-await a multi-second step inside the held request, so instead of
|
|
247
|
+
* `stepHost` calling `engine.resume` inline it hands the posted result to this
|
|
248
|
+
* hook to SETTLE-AND-ENQUEUE (record it durably, schedule the compute in a
|
|
249
|
+
* returning activation) and returns the {@link StepWorking} arm PROMPTLY. A later
|
|
250
|
+
* pull re-reads the durable next-step once {@link DeferResumeHook.settled} reports
|
|
251
|
+
* the enqueued compute has landed.
|
|
252
|
+
*
|
|
253
|
+
* Providing this hook is what flips `stepHost` from the 2-arm inline contract to
|
|
254
|
+
* the 3-arm deferred contract — see the `stepHost` overloads. Omit it and the
|
|
255
|
+
* host stays byte-identical inline.
|
|
256
|
+
*/
|
|
257
|
+
interface DeferResumeHook<R> {
|
|
258
|
+
/**
|
|
259
|
+
* SETTLE-AND-ENQUEUE the posted result to resume OUT of the held request:
|
|
260
|
+
* durably record it and schedule the compute (e.g. `ctx.storage.setAlarm` or a
|
|
261
|
+
* queue) so a returning activation runs `engine.resume`. MUST return promptly —
|
|
262
|
+
* it never awaits the step compute. Called at most once per distinct `callId`
|
|
263
|
+
* (`stepHost` dedupes re-POSTs through `idempotent-intake` first), so a re-POST
|
|
264
|
+
* of the same step does not re-enqueue.
|
|
265
|
+
*/
|
|
266
|
+
enqueue(runId: string, posted: StepResult<R>): void | Promise<void>;
|
|
267
|
+
/**
|
|
268
|
+
* Has the enqueued resume for `callId` COMPLETED and been written to the
|
|
269
|
+
* durable checkpoint? `false` ⇒ the held request returns {@link StepWorking}
|
|
270
|
+
* (poll again); `true` ⇒ `stepHost` reads the next step / terminal output off
|
|
271
|
+
* the now-advanced checkpoint, exactly as the inline path does.
|
|
272
|
+
*/
|
|
273
|
+
settled(runId: string, callId: string): boolean | Promise<boolean>;
|
|
274
|
+
/**
|
|
275
|
+
* Optional advisory re-poll delay (ms) surfaced on the {@link StepWorking} arm
|
|
276
|
+
* so the hands can back off to the host's expected step latency.
|
|
277
|
+
*/
|
|
278
|
+
readonly retryAfterMs?: number;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* `StepHostConfig` with the defer-resume hook engaged — the presence of
|
|
282
|
+
* `deferResume` is the type-level switch that selects the 3-arm response (see the
|
|
283
|
+
* `stepHost` overloads). Extends the inline config, so the alarm tuning is shared.
|
|
284
|
+
*/
|
|
285
|
+
interface DeferStepHostConfig<R> extends StepHostConfig {
|
|
286
|
+
readonly deferResume: DeferResumeHook<R>;
|
|
287
|
+
}
|
|
205
288
|
/**
|
|
206
289
|
* A structured `/step` outcome `stepHost` returns — the response body plus the
|
|
207
290
|
* HTTP status the consumer's route should send. Kept transport-agnostic (no
|
|
@@ -216,6 +299,21 @@ type StepOutcome<I, O> = {
|
|
|
216
299
|
readonly error: string;
|
|
217
300
|
};
|
|
218
301
|
};
|
|
302
|
+
/**
|
|
303
|
+
* The `/step` outcome a DEFER-RESUME host returns — like {@link StepOutcome} but
|
|
304
|
+
* its 200 body is the 3-arm {@link DeferredStepResponse} (it can carry the
|
|
305
|
+
* not-ready arm). Returned only from the `deferResume` overload of `stepHost`;
|
|
306
|
+
* the inline overload keeps the narrower {@link StepOutcome}.
|
|
307
|
+
*/
|
|
308
|
+
type DeferredStepOutcome<I, O> = {
|
|
309
|
+
readonly status: 200;
|
|
310
|
+
readonly body: DeferredStepResponse<I, O>;
|
|
311
|
+
} | {
|
|
312
|
+
readonly status: 401 | 404;
|
|
313
|
+
readonly body: {
|
|
314
|
+
readonly error: string;
|
|
315
|
+
};
|
|
316
|
+
};
|
|
219
317
|
/**
|
|
220
318
|
* Build the `/step` handler for a DO that drives one or more runs over the pull
|
|
221
319
|
* carrier. Returns `handle(request, now)` — call it from the DO's `/step`
|
|
@@ -233,11 +331,20 @@ type StepOutcome<I, O> = {
|
|
|
233
331
|
* @param engine the run-specific operations
|
|
234
332
|
* @param config alarm tuning
|
|
235
333
|
*/
|
|
334
|
+
declare function stepHost<R, I, O>(ctx: StepCtx, engine: StepEngine<R, I, O>, config: DeferStepHostConfig<R>): {
|
|
335
|
+
handle(request: StepRequest<R>, now: number): Promise<DeferredStepOutcome<I, O>>;
|
|
336
|
+
};
|
|
236
337
|
declare function stepHost<R, I, O>(ctx: StepCtx, engine: StepEngine<R, I, O>, config?: StepHostConfig): {
|
|
237
338
|
handle(request: StepRequest<R>, now: number): Promise<StepOutcome<I, O>>;
|
|
238
339
|
};
|
|
239
|
-
/**
|
|
240
|
-
|
|
340
|
+
/**
|
|
341
|
+
* The `/step` transport the hands POST through (injected; faked in tests).
|
|
342
|
+
* Returns the 3-arm {@link DeferredStepResponse} — a defer-resume host may answer
|
|
343
|
+
* with the not-ready arm, which {@link runStepLoop} re-polls. An inline host's
|
|
344
|
+
* 2-arm transport (`=> Promise<StepResponse>`) is assignable here (the narrower
|
|
345
|
+
* return widens), so existing inline adopters are unaffected.
|
|
346
|
+
*/
|
|
347
|
+
type StepTransport<R, I, O> = (request: StepRequest<R>) => Promise<DeferredStepResponse<I, O>>;
|
|
241
348
|
/** Execute one tool call and produce its result (the consumer's hands). */
|
|
242
349
|
type ExecuteStep<I, R> = (step: NextStep<I>) => Promise<R> | R;
|
|
243
350
|
/** Tuning for {@link runStepLoop}. */
|
|
@@ -284,6 +391,11 @@ type StepLoopOutcome<O> = {
|
|
|
284
391
|
* not re-executed; its prior result is re-POSTed. Closes the "the host
|
|
285
392
|
* re-handed the same step after a duplicate settle" path (the host side is
|
|
286
393
|
* idempotent; the hands match it).
|
|
394
|
+
* - **not-ready re-poll** — a {@link StepWorking} arm (a defer-resume host
|
|
395
|
+
* computing the step out-of-band) is not a step: the loop re-POSTs the SAME
|
|
396
|
+
* `posted` after a backoff (honoring the host's advisory `retryAfterMs`),
|
|
397
|
+
* until a real step / done arrives. Bounded only by the absolute deadline —
|
|
398
|
+
* an inline host never returns this arm, so its drive is unchanged.
|
|
287
399
|
*/
|
|
288
400
|
declare function runStepLoop<R, I, O>(token: string, runId: string, transport: StepTransport<R, I, O>, execute: ExecuteStep<I, R>, config: RunStepLoopConfig): Promise<StepLoopOutcome<O>>;
|
|
289
401
|
|
|
@@ -662,6 +774,18 @@ interface ProjectionId {
|
|
|
662
774
|
}
|
|
663
775
|
/** Render a `ProjectionId` to its canonical `name-key` string. */
|
|
664
776
|
declare function projectionIdString(id: ProjectionId): string;
|
|
777
|
+
/** Context handed to a {@link ProjectionOnError} sink alongside the throw. */
|
|
778
|
+
interface ProjectionErrorContext {
|
|
779
|
+
/** The id of the projection whose `apply`/`emit` threw. */
|
|
780
|
+
readonly id: ProjectionId;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Sink for a projection `apply`/`emit` throw the driver isolated. Injected into
|
|
784
|
+
* {@link projectionRegistry}; called with the original error and the id of the
|
|
785
|
+
* failing projection so a broken projection surfaces instead of being silently
|
|
786
|
+
* discarded (errors-are-data). Should be total.
|
|
787
|
+
*/
|
|
788
|
+
type ProjectionOnError = (error: unknown, context: ProjectionErrorContext) => void;
|
|
665
789
|
/**
|
|
666
790
|
* One unit the projection driver presents to a projection's `apply`. Mirrors
|
|
667
791
|
* the `(msg, model)` pair `runtime.observe` fans out, plus the exclusive
|
|
@@ -750,9 +874,11 @@ interface ProjectionRunner<Model, Msg extends {
|
|
|
750
874
|
* `startOffset` (the driver's offset guard enforces exclusivity even if an
|
|
751
875
|
* already-applied update is re-presented).
|
|
752
876
|
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
877
|
+
* The runner never emits the seed: a fresh runner holds `initial` but does not
|
|
878
|
+
* push it at the sink until the first real update lands (an SSE client that just
|
|
879
|
+
* connected wants the next event, not a replay of the seed), and `reset()`
|
|
880
|
+
* likewise re-seeds `view`/`offset` silently — `emit` fires only from `present`
|
|
881
|
+
* when a strictly-newer update is folded.
|
|
756
882
|
*/
|
|
757
883
|
declare function runProjection<Model, Msg extends {
|
|
758
884
|
type: string;
|
|
@@ -771,7 +897,8 @@ interface ProjectionRegistry<Model, Msg extends {
|
|
|
771
897
|
* Present one update to EVERY registered runner. Each folds independently; an
|
|
772
898
|
* `apply`/`emit` throw in one runner is caught so it cannot strand the others
|
|
773
899
|
* (errors-are-data: a broken projection is dropped from THIS update, not
|
|
774
|
-
* allowed to corrupt siblings)
|
|
900
|
+
* allowed to corrupt siblings) and routed to the registry's `onError` sink so
|
|
901
|
+
* it surfaces rather than vanishing. Returns the offset assigned to this update.
|
|
775
902
|
*/
|
|
776
903
|
dispatch(msg: Msg | null, model: Model): number;
|
|
777
904
|
/** The current monotonic offset (the last dispatched update's position). */
|
|
@@ -779,10 +906,15 @@ interface ProjectionRegistry<Model, Msg extends {
|
|
|
779
906
|
/** The registered runners (introspection / tests). */
|
|
780
907
|
runners(): readonly ProjectionRunner<Model, Msg, unknown>[];
|
|
781
908
|
}
|
|
782
|
-
/**
|
|
909
|
+
/**
|
|
910
|
+
* Build an empty {@link ProjectionRegistry}. Pass an `onError` sink to receive
|
|
911
|
+
* any `apply`/`emit` throw the driver isolates — without it a broken projection
|
|
912
|
+
* is still isolated from its siblings, but its failure is not surfaced (the
|
|
913
|
+
* substrate's `run(machine, { onError })` is the natural source to forward).
|
|
914
|
+
*/
|
|
783
915
|
declare function projectionRegistry<Model, Msg extends {
|
|
784
916
|
type: string;
|
|
785
|
-
}>(): ProjectionRegistry<Model, Msg>;
|
|
917
|
+
}>(onError?: ProjectionOnError): ProjectionRegistry<Model, Msg>;
|
|
786
918
|
/**
|
|
787
919
|
* Wire a {@link ProjectionRegistry} to a runtime's transition stream. The boot
|
|
788
920
|
* update (the initial state, offset 0) is presented via `onBoot`; every applied
|
|
@@ -828,166 +960,202 @@ declare function rebuildProjection<Model, Msg extends {
|
|
|
828
960
|
}[]): ProjectionRunner<Model, Msg, View>;
|
|
829
961
|
|
|
830
962
|
/**
|
|
831
|
-
*
|
|
832
|
-
* `
|
|
833
|
-
*
|
|
834
|
-
* `doStore` (in `./index`) is the durability half. This file is the rest of the
|
|
835
|
-
* boilerplate a DO consumer hand-rolls every time it drives a tea agent over a
|
|
836
|
-
* remote transport: the deferred-tool gateway, auto-boot on rehydrate, the
|
|
837
|
-
* command-runner WS accept, the runtime→SSE plumbing, and a clean read of a
|
|
838
|
-
* run's terminal output. Each export below names exactly the hand-rolled piece
|
|
839
|
-
* it deletes from the consumer.
|
|
840
|
-
*
|
|
841
|
-
* ## The transport model: ONE Sub type, gateway-bridged I/O
|
|
842
|
-
*
|
|
843
|
-
* `createAgent().toMachine()` fixes the machine's Sub type to `DeadlineSub` —
|
|
844
|
-
* the agent owns the retry + watchdog timers and nothing else. That is the
|
|
845
|
-
* WHOLE Sub model for a DO-hosted agent, and it is coherent because the
|
|
846
|
-
* transport deliberately does NOT live in the Sub system:
|
|
847
|
-
*
|
|
848
|
-
* - Inbound WebSocket frames are bridged straight into `gateway.settle(...)`
|
|
849
|
-
* from the DO's `webSocketMessage` lifecycle method — a direct dispatch,
|
|
850
|
-
* not a Sub.
|
|
851
|
-
* - The per-tool deadline is a `setTimeout` inside the gateway — a timer, not
|
|
852
|
-
* a DO alarm.
|
|
853
|
-
*
|
|
854
|
-
* The deferred-tool gateway owns each tool round-trip as a Promise the interpret
|
|
855
|
-
* cell awaits; the transport bridges results into `dispatch` from outside the
|
|
856
|
-
* Sub system. The agent's Sub type stays exactly `DeadlineSub` — there is no
|
|
857
|
-
* competing DO-native Sub variant to union in, and the host needs none. This is
|
|
858
|
-
* the single transport model for `@demlik/tea/do` (#52); see
|
|
859
|
-
* `.patterns/tea/durable-actors.md` for the host-layer north star.
|
|
963
|
+
* The runtime→SSE plumbing — the hub, its projection expression, and the
|
|
964
|
+
* semantic-event adapter. See `./host` for the transport-model rationale.
|
|
860
965
|
*/
|
|
861
966
|
|
|
862
967
|
/**
|
|
863
|
-
*
|
|
864
|
-
* `
|
|
968
|
+
* A set of SSE sinks plus the plumbing to fan an event out to all of them and
|
|
969
|
+
* to open a `text/event-stream` Response wired to a fresh sink.
|
|
865
970
|
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
* - `settle(callId, result)` — the transport calls this on a reply; resolves
|
|
872
|
-
* the awaited promise. A settle for an unknown / already-settled id is a
|
|
873
|
-
* no-op (the callId guard the consumer used to keep in its pending Map).
|
|
874
|
-
* - `fail(callId, reason)` — settle the round-trip as an error on demand
|
|
875
|
-
* (e.g. a transport-level close). Same unknown-id no-op.
|
|
876
|
-
* - `inFlight()` — the callIds with an open round-trip (tests / introspection).
|
|
971
|
+
* The consumer:
|
|
972
|
+
* 1. holds one `SseHub<E>` per DO,
|
|
973
|
+
* 2. calls `hub.emit(event)` from its Msg→event mapping (driven off
|
|
974
|
+
* `runtime.observe`), and
|
|
975
|
+
* 3. returns `hub.open()` from the `/sse` route.
|
|
877
976
|
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
977
|
+
* `register(sink)` exists for tests: subscribe a sink directly and observe the
|
|
978
|
+
* exact event sequence a live client would receive (a live `text/event-stream`
|
|
979
|
+
* fights vitest-pool-workers teardown). Deletes the consumer's `sseSinks` set,
|
|
980
|
+
* `openSse`, `emitSse`, and `onSseEvent`.
|
|
881
981
|
*/
|
|
882
|
-
interface
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
inFlight(): readonly string[];
|
|
982
|
+
interface SseHub<E> {
|
|
983
|
+
emit(event: E): void;
|
|
984
|
+
open(): Response;
|
|
985
|
+
register(sink: (event: E) => void): () => void;
|
|
887
986
|
}
|
|
987
|
+
/** Build an SSE hub for event type `E` (the consumer's domain event shape). */
|
|
988
|
+
declare function sseHub<E>(): SseHub<E>;
|
|
888
989
|
/**
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
990
|
+
* Express an {@link SseHub} as a {@link Projection}. The projection's view is
|
|
991
|
+
* the latest emitted SSE event (or `null` before the first); `apply` maps each
|
|
992
|
+
* write-model update to an event via `toEvent` and emits it through `hub.emit`,
|
|
993
|
+
* so the SSE stream is driven by the SAME hub `sseHub()` returns — no API
|
|
994
|
+
* change for the `/sse` route (`hub.open()`) or any existing caller.
|
|
892
995
|
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
*
|
|
996
|
+
* - `toEvent(msg, model)` returns the SSE event for this update, or `null` to
|
|
997
|
+
* skip it (the view stays, nothing is emitted).
|
|
998
|
+
* - `name`/`key` default to `"sse"`/`"main"`; override for a second SSE view.
|
|
999
|
+
*
|
|
1000
|
+
* @example
|
|
1001
|
+
* const hub = sseHub<MyEvent>();
|
|
1002
|
+
* const registry = projectionRegistry<MyState, MyMsg>();
|
|
1003
|
+
* registry.register(sseProjection(hub, (msg) =>
|
|
1004
|
+
* msg?.type === "turn_done" ? { kind: "turn", ... } : null,
|
|
1005
|
+
* ));
|
|
1006
|
+
* driveProjections(registry, runtime); // SSE is now one projection
|
|
1007
|
+
* // /sse route unchanged:
|
|
1008
|
+
* return hub.open();
|
|
897
1009
|
*/
|
|
898
|
-
declare function
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
callId: string;
|
|
905
|
-
}>;
|
|
906
|
-
}
|
|
1010
|
+
declare function sseProjection<Model, Msg extends {
|
|
1011
|
+
type: string;
|
|
1012
|
+
}, E>(hub: SseHub<E>, toEvent: (msg: Msg | null, model: Model) => E | null, id?: {
|
|
1013
|
+
name?: string;
|
|
1014
|
+
key?: string;
|
|
1015
|
+
}): Projection<Model, Msg, E | null>;
|
|
907
1016
|
/**
|
|
908
|
-
*
|
|
909
|
-
* `
|
|
910
|
-
*
|
|
1017
|
+
* Wire an {@link SseHub} to a runtime's semantic {@link AgentEvent} stream (#47).
|
|
1018
|
+
* For each event `type` the runtime can emit, subscribes via `runtime.on` and
|
|
1019
|
+
* pushes `toFrame(event)` at the hub (returning `null` skips that event). The
|
|
1020
|
+
* runtime MUST have been built with `run(machine, { events: agentEvents() })`,
|
|
1021
|
+
* which is what makes `on` deliver the `AgentEvent`s. Returns a cleanup that
|
|
1022
|
+
* detaches every subscription.
|
|
911
1023
|
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
* - `reissue(callId, send, deadlineMs)` — re-arm a surviving round-trip on
|
|
917
|
-
* activation. Idempotent: the gateway's own `await` returns the open promise
|
|
918
|
-
* without re-firing `send` for an in-flight `callId`, so re-issuing an id
|
|
919
|
-
* that is somehow still pending is a no-op (mirrors the boot-reconcile
|
|
920
|
-
* re-fire). The effect is already owed, so no second `effect_owed` is
|
|
921
|
-
* recorded.
|
|
1024
|
+
* This is the runtime→SSE adapter expressed over the typed event channel: no
|
|
1025
|
+
* `observe` firehose, no private-Msg coupling. `hub.open()` and every existing
|
|
1026
|
+
* `sseHub` caller are unchanged — only the SOURCE of `hub.emit` moves from a
|
|
1027
|
+
* hand-rolled Msg switch to `on`.
|
|
922
1028
|
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
1029
|
+
* @example
|
|
1030
|
+
* const hub = sseHub<MyFrame>();
|
|
1031
|
+
* const runtime = await run(machine, { ctx, events: agentEvents() }).ready;
|
|
1032
|
+
* const off = sseFromAgentEvents(runtime, hub, (e) =>
|
|
1033
|
+
* e.type === "RunDone" ? { kind: "done", output: e.output } : null,
|
|
1034
|
+
* );
|
|
1035
|
+
* // /sse route unchanged:
|
|
1036
|
+
* return hub.open();
|
|
925
1037
|
*/
|
|
926
|
-
declare function
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
recordConfirmed(callId: string, event: EffectConfirmed): void;
|
|
933
|
-
}): DurableDeferredGateway<R>;
|
|
1038
|
+
declare function sseFromAgentEvents<R, Frame>(runtime: {
|
|
1039
|
+
on<K extends AgentEvent<R>["type"]>(type: K, handler: (event: Extract<AgentEvent<R>, {
|
|
1040
|
+
type: K;
|
|
1041
|
+
}>) => void): () => void;
|
|
1042
|
+
}, hub: SseHub<Frame>, toFrame: (event: AgentEvent<R>) => Frame | null): () => void;
|
|
1043
|
+
|
|
934
1044
|
/**
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
* - `resumeMsg(now)` — the TYPED constructor for the single resume Msg (never a
|
|
941
|
-
* hand-built `{ type: … }` literal); the `agentBootMsg` shape lifted out so a
|
|
942
|
-
* non-agent grain supplies its own boot Msg constructor.
|
|
1045
|
+
* `createAgentHost` — the runtime+events+autoBoot assembly + the test seam,
|
|
1046
|
+
* ONCE (issue #53). The facade over the concern modules: the deferred gateway
|
|
1047
|
+
* (`./deferred-gateway`), the WS carrier (`./command-socket`), the SSE plumbing
|
|
1048
|
+
* (`./sse`), and the cold-wake resume (`./resume`). See `./host` for the
|
|
1049
|
+
* transport-model rationale.
|
|
943
1050
|
*/
|
|
944
|
-
|
|
945
|
-
type: string;
|
|
946
|
-
}> {
|
|
947
|
-
readonly isResumable: (state: S) => boolean;
|
|
948
|
-
readonly resumeMsg: (now: number) => M;
|
|
949
|
-
}
|
|
1051
|
+
|
|
950
1052
|
/**
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
* `await ready` is the single boot gate (issue #45): `getState()` is total only
|
|
959
|
-
* on the booted `Runtime`, so state cannot be inspected before boot populates
|
|
960
|
-
* it. `now` is injected (the sole boot clock read) so a test can pin it.
|
|
1053
|
+
* What the consumer supplies to {@link createAgentHost} — the domain mappings,
|
|
1054
|
+
* nothing of the wiring. `Stage`/`P`/`O`/`R` are the agent slice's type
|
|
1055
|
+
* parameters (the consumer names them once); `Frame` is its SSE event shape.
|
|
1056
|
+
* `C`/`U`/`Ctx` are the machine's own Cmd/Sub/Ctx — inferred from
|
|
1057
|
+
* `buildMachine` at the `createAgentHost` call site (never name them by hand;
|
|
1058
|
+
* an erased `any` here cost consumers all checking on this layer — #278).
|
|
961
1059
|
*/
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1060
|
+
interface AgentHostConfig<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown> {
|
|
1061
|
+
/**
|
|
1062
|
+
* Build the wired agent machine. Called once per host build (per activation),
|
|
1063
|
+
* AFTER the previous runtime — if any — was torn down. The consumer wires its
|
|
1064
|
+
* per-tool interpret here (`agent.toMachine({ toolInterpret })`).
|
|
1065
|
+
*/
|
|
1066
|
+
readonly buildMachine: () => Machine<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, C, U, Ctx>;
|
|
1067
|
+
/** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */
|
|
1068
|
+
readonly store: Store<AgentState<Stage, P, O, R>>;
|
|
1069
|
+
/** The Ctx the machine threads to its interpret cells — checked against `buildMachine`'s. */
|
|
1070
|
+
readonly ctx: Ctx;
|
|
1071
|
+
/**
|
|
1072
|
+
* The run-terminality predicate (#46) — what makes `result()` first-class.
|
|
1073
|
+
* Defaults to the agent's own terminal phases (`done` / `failed`).
|
|
1074
|
+
*/
|
|
1075
|
+
readonly terminal?: (state: AgentState<Stage, P, O, R>) => boolean;
|
|
1076
|
+
/**
|
|
1077
|
+
* Map one semantic {@link AgentEvent} to the consumer's SSE frame, or `null`
|
|
1078
|
+
* to skip it. This is the ONLY place the consumer touches the SSE seam — the
|
|
1079
|
+
* host owns the subscription wiring (`sseFromAgentEvents`) and the hub.
|
|
1080
|
+
*/
|
|
1081
|
+
readonly toSseFrame: (event: AgentEvent<R>) => Frame | null;
|
|
1082
|
+
/** Clock injected for `autoBoot` (the host's only boot clock read). */
|
|
1083
|
+
readonly now?: () => number;
|
|
1084
|
+
}
|
|
967
1085
|
/**
|
|
968
|
-
*
|
|
969
|
-
*
|
|
1086
|
+
* The assembled host: the runtime cell + the SSE hub + the framework test seam,
|
|
1087
|
+
* owned ONCE. A DO holds one `AgentHost` and delegates to it; its own surface
|
|
1088
|
+
* shrinks to the domain (the gateway, the WS bridge, the command-send mapping).
|
|
970
1089
|
*
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
1090
|
+
* @typeParam Stage Pipeline stage type.
|
|
1091
|
+
* @typeParam P Brain-call purposes.
|
|
1092
|
+
* @typeParam O Per-purpose outputs (bound to {@link AgentTurn}).
|
|
1093
|
+
* @typeParam R Tool result type.
|
|
1094
|
+
* @typeParam Frame The consumer's SSE event shape.
|
|
976
1095
|
*/
|
|
977
|
-
|
|
978
|
-
/**
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1096
|
+
interface AgentHost<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame> {
|
|
1097
|
+
/**
|
|
1098
|
+
* Get (or build) the booted runtime. Build-once-boot-once: the first call
|
|
1099
|
+
* builds the machine, wires SSE off the semantic event stream, awaits the boot
|
|
1100
|
+
* gate, runs the `autoBoot` re-fire for a rehydrated suspended run, and caches
|
|
1101
|
+
* the booted `Runtime`; subsequent calls return the cached handle. The single
|
|
1102
|
+
* assembly every consumer used to hand-roll in `getRuntime()`.
|
|
1103
|
+
*/
|
|
1104
|
+
runtime(): Promise<Runtime<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, AgentEvent<R>>>;
|
|
1105
|
+
/**
|
|
1106
|
+
* The agent's lifecycle status (#49) — the ONE typed channel a consumer reads
|
|
1107
|
+
* instead of re-deriving `run.phase` / `awaiting` by hand. Ensures the runtime
|
|
1108
|
+
* is built + boot-reconciled first. Replaces the per-consumer `isSuspended` /
|
|
1109
|
+
* `runPhase` test methods (read `status().kind`).
|
|
1110
|
+
*/
|
|
1111
|
+
status(): Promise<AgentStatus<Stage>>;
|
|
1112
|
+
/**
|
|
1113
|
+
* The run's terminal State (#46), or `undefined` while in flight. Read the
|
|
1114
|
+
* run's product off it (e.g. `result()?.output`). Replaces the per-consumer
|
|
1115
|
+
* `verdict` plumbing's source. Ensures the runtime is built first.
|
|
1116
|
+
*/
|
|
1117
|
+
result(): Promise<AgentState<Stage, P, O, R> | undefined>;
|
|
1118
|
+
/**
|
|
1119
|
+
* The SSE hub — the route (`host.sse.open()`) AND the test seam
|
|
1120
|
+
* (`host.sse.register(sink)`). The host drives `hub.emit` off the semantic
|
|
1121
|
+
* event stream; the consumer never touches it except to open / register.
|
|
1122
|
+
*/
|
|
1123
|
+
readonly sse: SseHub<Frame>;
|
|
1124
|
+
/**
|
|
1125
|
+
* Tear down the cached runtime (drain its tail via `stop()`, drop the handle)
|
|
1126
|
+
* so the next `runtime()` rebuilds from storage. The framework `settle` test
|
|
1127
|
+
* seam, owned once.
|
|
1128
|
+
*/
|
|
1129
|
+
reset(): Promise<void>;
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Build an {@link AgentHost} from the consumer's domain mappings. Owns the
|
|
1133
|
+
* runtime+events+autoBoot assembly and the framework test seam — the wiring
|
|
1134
|
+
* issue #53 had every consumer re-write.
|
|
985
1135
|
*
|
|
986
|
-
*
|
|
1136
|
+
* @example
|
|
1137
|
+
* const host = createAgentHost<Stage, Purpose, Outputs, ClientResult, SseEvent>({
|
|
1138
|
+
* buildMachine: () => agent.toMachine<Ctx>({ toolInterpret }),
|
|
1139
|
+
* store: doStore(storage, parse),
|
|
1140
|
+
* ctx,
|
|
1141
|
+
* toSseFrame: (e) =>
|
|
1142
|
+
* e.type === "ToolSettled" ? { kind: "result", ...} :
|
|
1143
|
+
* e.type === "TurnSettled" && e.turn.toolCalls.length === 0
|
|
1144
|
+
* ? { kind: "verdict", verdict: e.turn.content } :
|
|
1145
|
+
* e.type === "RunDone" ? { kind: "phase", phase: "done" } : null,
|
|
1146
|
+
* });
|
|
1147
|
+
* // route: return host.sse.open();
|
|
1148
|
+
* // start: await (await host.runtime()).dispatch({ type: "agent_start", ... });
|
|
1149
|
+
* // test: (await host.status()).kind === "suspended"
|
|
1150
|
+
*/
|
|
1151
|
+
declare function createAgentHost<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown>(config: AgentHostConfig<Stage, P, O, R, Frame, C, U, Ctx>): AgentHost<Stage, P, O, R, Frame>;
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* The command-runner WebSocket host + broadcast — the WS half of the
|
|
1155
|
+
* `@demlik/tea/do` transport, in both the resident (`acceptCommandSocket`) and
|
|
1156
|
+
* hibernation-aware (`acceptDurableCommandSocket`) forms. See `./host` for the
|
|
1157
|
+
* transport-model rationale (ONE Sub type, gateway-bridged I/O).
|
|
987
1158
|
*/
|
|
988
|
-
declare function autoBoot<Stage, P extends string, O extends Record<P, unknown>, R, E extends {
|
|
989
|
-
type: string;
|
|
990
|
-
} = never>(booting: BootingRuntime<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, E>, now?: () => number): Promise<void>;
|
|
991
1159
|
/**
|
|
992
1160
|
* Accept a command-runner WebSocket on the DO's `fetch`. Adds the server end to
|
|
993
1161
|
* `clients`, wires `message` → `onFrame(data)` (string frames only) and
|
|
@@ -1001,6 +1169,11 @@ declare function acceptCommandSocket(clients: Set<WebSocket>, onFrame: (data: st
|
|
|
1001
1169
|
* Broadcast a JSON frame to every connected command-runner socket. A dead
|
|
1002
1170
|
* socket is skipped (dropped on its own `close` event). Lifts the consumer's
|
|
1003
1171
|
* fan-out loop in `sendCommand`.
|
|
1172
|
+
*
|
|
1173
|
+
* Thin delegation to {@link broadcastFrame} — the general fan-out (serialize
|
|
1174
|
+
* once, send to every OPEN socket, skip a closed/errored one). The
|
|
1175
|
+
* {@link BroadcastReport} it returns is discarded here to keep the `void`
|
|
1176
|
+
* command-socket signature.
|
|
1004
1177
|
*/
|
|
1005
1178
|
declare function broadcast(clients: Set<WebSocket>, frame: unknown): void;
|
|
1006
1179
|
/**
|
|
@@ -1032,8 +1205,92 @@ declare function acceptDurableCommandSocket(ctx: HibernatableCtx): Response;
|
|
|
1032
1205
|
* set is read from `ctx.getWebSockets()` (the runtime repopulates it after a
|
|
1033
1206
|
* wake), NOT a resident `Set<WebSocket>` — so it is correct immediately after an
|
|
1034
1207
|
* eviction, before any other lifecycle method has run. A dead socket is skipped.
|
|
1208
|
+
*
|
|
1209
|
+
* Thin delegation to {@link broadcastFrame} over `ctx.getWebSockets()` — the
|
|
1210
|
+
* general fan-out (serialize once, send to every OPEN socket, skip a
|
|
1211
|
+
* closed/errored one). The {@link BroadcastReport} it returns is discarded here
|
|
1212
|
+
* to keep the `void` command-socket signature.
|
|
1035
1213
|
*/
|
|
1036
1214
|
declare function broadcastHibernatable(ctx: HibernatableCtx, frame: unknown): void;
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* The deferred-tool gateway + its durable wrappers — the Promise-owning half of
|
|
1218
|
+
* the `@demlik/tea/do` transport. See `./host` for the transport-model
|
|
1219
|
+
* rationale (ONE Sub type, gateway-bridged I/O).
|
|
1220
|
+
*/
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* The deferred-tool gateway. `R` is the consumer's tool-result type (what
|
|
1224
|
+
* `agent_tool_ok` carries).
|
|
1225
|
+
*
|
|
1226
|
+
* - `await(callId, send, deadlineMs)` — register the round-trip, fire `send`,
|
|
1227
|
+
* return the Promise the reply (or the deadline) settles. Idempotent per
|
|
1228
|
+
* `callId`: a second call for an in-flight id returns the SAME promise
|
|
1229
|
+
* without re-firing `send` (the boot-reconcile re-fire is a no-op at the
|
|
1230
|
+
* gateway, mirroring the agent's idempotent boot).
|
|
1231
|
+
* - `settle(callId, result)` — the transport calls this on a reply; resolves
|
|
1232
|
+
* the awaited promise. A settle for an unknown / already-settled id is a
|
|
1233
|
+
* no-op (the callId guard the consumer used to keep in its pending Map).
|
|
1234
|
+
* - `fail(callId, reason)` — settle the round-trip as an error on demand
|
|
1235
|
+
* (e.g. a transport-level close). Same unknown-id no-op.
|
|
1236
|
+
* - `inFlight()` — the callIds with an open round-trip (tests / introspection).
|
|
1237
|
+
*
|
|
1238
|
+
* The deadline → reject path is what makes "timeout is data": the tool cell's
|
|
1239
|
+
* `catch` maps the rejection to `agent_tool_err`, recorded on the conversation,
|
|
1240
|
+
* never a hang.
|
|
1241
|
+
*/
|
|
1242
|
+
interface DeferredGateway<R> {
|
|
1243
|
+
await(callId: string, send: () => void, deadlineMs: number): Promise<R>;
|
|
1244
|
+
settle(callId: string, result: R): void;
|
|
1245
|
+
fail(callId: string, reason: string): void;
|
|
1246
|
+
inFlight(): readonly string[];
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Build a deferred-tool gateway. No clock / RNG captured at construction — the
|
|
1250
|
+
* deadline is per-call (`await(..., deadlineMs)`), so one gateway serves every
|
|
1251
|
+
* tool family with its own timeout.
|
|
1252
|
+
*
|
|
1253
|
+
* Deletes from the consumer: the `pending` Map, the `Pending` interface, the
|
|
1254
|
+
* `awaitClient` method, the `forceTimeout` plumbing, and the `deliverResult`
|
|
1255
|
+
* callId-guard body — all of it collapses to this object plus the tool cell's
|
|
1256
|
+
* `await` + try/catch.
|
|
1257
|
+
*/
|
|
1258
|
+
declare function deferredGateway<R>(): DeferredGateway<R>;
|
|
1259
|
+
/** A `callId`-keyed durable round-trip: maps the gateway's string id to the
|
|
1260
|
+
* ledger's monotonic delivery id so a `settle(callId)` finds the owed entry. */
|
|
1261
|
+
interface DurableDeferredGateway<R> extends DeferredGateway<R> {
|
|
1262
|
+
/** The ledger recorder this gateway feeds (for `surviving()` on activation). */
|
|
1263
|
+
readonly recorder: PendingEffectsRecorder<{
|
|
1264
|
+
callId: string;
|
|
1265
|
+
}>;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Wrap a `DeferredGateway<R>` so every round-trip is also recorded in a durable
|
|
1269
|
+
* `PendingEffectsRecorder`. The in-memory gateway is the fast path; the recorder
|
|
1270
|
+
* is the durability slice.
|
|
1271
|
+
*
|
|
1272
|
+
* - `recordOwed(callId, event)` — fires from `await`, BEFORE `send`, carrying
|
|
1273
|
+
* the `effect_owed` event the consumer must persist (then deliver).
|
|
1274
|
+
* - `recordConfirmed(callId, event)` — fires from `settle`/`fail` for a known
|
|
1275
|
+
* id, carrying the `effect_confirmed` event to persist.
|
|
1276
|
+
* - `reissue(callId, send, deadlineMs)` — re-arm a surviving round-trip on
|
|
1277
|
+
* activation. Idempotent: the gateway's own `await` returns the open promise
|
|
1278
|
+
* without re-firing `send` for an in-flight `callId`, so re-issuing an id
|
|
1279
|
+
* that is somehow still pending is a no-op (mirrors the boot-reconcile
|
|
1280
|
+
* re-fire). The effect is already owed, so no second `effect_owed` is
|
|
1281
|
+
* recorded.
|
|
1282
|
+
*
|
|
1283
|
+
* The effect payload the ledger stores is the `callId` (the correlation key the
|
|
1284
|
+
* consumer round-trips); the consumer maps it back to its tool-call context.
|
|
1285
|
+
*/
|
|
1286
|
+
declare function durableDeferredGateway<R>(inner: DeferredGateway<R>, recorder: PendingEffectsRecorder<{
|
|
1287
|
+
callId: string;
|
|
1288
|
+
}>, hooks: {
|
|
1289
|
+
recordOwed(callId: string, event: EffectOwed<{
|
|
1290
|
+
callId: string;
|
|
1291
|
+
}>): void;
|
|
1292
|
+
recordConfirmed(callId: string, event: EffectConfirmed): void;
|
|
1293
|
+
}): DurableDeferredGateway<R>;
|
|
1037
1294
|
/**
|
|
1038
1295
|
* The durable command carrier — a {@link DurableDeferredGateway} whose every
|
|
1039
1296
|
* tool round-trip is also a durable owed effect.
|
|
@@ -1045,18 +1302,15 @@ declare function broadcastHibernatable(ctx: HibernatableCtx, frame: unknown): vo
|
|
|
1045
1302
|
* `recordOwed`) to PERSIST before the send, and `settle`/`fail` hand it an
|
|
1046
1303
|
* `effect_confirmed` (via `recordConfirmed`) to persist. On the next activation
|
|
1047
1304
|
* the consumer reads its persisted ledger events back and calls
|
|
1048
|
-
*
|
|
1305
|
+
* `reissueSurvivingEffects` (see `./resume`) to re-fire the survivors.
|
|
1049
1306
|
*
|
|
1050
|
-
* It
|
|
1307
|
+
* It IS a `DurableDeferredGateway` (so it is a drop-in for the gateway the
|
|
1051
1308
|
* interpret cell already awaits) and exposes the underlying `recorder` for a
|
|
1052
|
-
* host that wants to read `surviving()` directly.
|
|
1309
|
+
* host that wants to read `surviving()` directly. The #91 "command carrier"
|
|
1310
|
+
* concept is exactly the durable gateway under a domain name — a type alias, not
|
|
1311
|
+
* a second byte-identical interface that could drift.
|
|
1053
1312
|
*/
|
|
1054
|
-
|
|
1055
|
-
/** The ledger recorder backing this carrier (for `surviving()` introspection). */
|
|
1056
|
-
readonly recorder: PendingEffectsRecorder<{
|
|
1057
|
-
callId: string;
|
|
1058
|
-
}>;
|
|
1059
|
-
}
|
|
1313
|
+
type DurableCommandCarrier<R> = DurableDeferredGateway<R>;
|
|
1060
1314
|
/**
|
|
1061
1315
|
* Build a durable command carrier over a (volatile) `DeferredGateway<R>` and a
|
|
1062
1316
|
* `PendingEffectsRecorder`. Thin assembly over {@link durableDeferredGateway}:
|
|
@@ -1083,6 +1337,71 @@ declare function durableCommandCarrier<R>(inner: DeferredGateway<R>, recorder: P
|
|
|
1083
1337
|
}>): void;
|
|
1084
1338
|
recordConfirmed(callId: string, event: EffectConfirmed): void;
|
|
1085
1339
|
}): DurableCommandCarrier<R>;
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* Cold-wake resume — the boot/hibernation re-fire path: the generalized
|
|
1343
|
+
* `bootResume` port, its agent specialization `autoBoot`, and the wake-time
|
|
1344
|
+
* re-emit of surviving tool round-trips. See `./host` for the transport-model
|
|
1345
|
+
* rationale.
|
|
1346
|
+
*/
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* The typed cold-wake resume port `bootResume` fires through — the agent's
|
|
1350
|
+
* `AgentBootPort` (issue #60) generalized to any DO-hosted machine (issue #231).
|
|
1351
|
+
*
|
|
1352
|
+
* - `isResumable(state)` — true iff the rehydrated State is mid-loop and needs a
|
|
1353
|
+
* resume dispatch. False on a fresh boot, which makes `bootResume` a no-op.
|
|
1354
|
+
* - `resumeMsg(now)` — the TYPED constructor for the single resume Msg (never a
|
|
1355
|
+
* hand-built `{ type: … }` literal); the `agentBootMsg` shape lifted out so a
|
|
1356
|
+
* non-agent grain supplies its own boot Msg constructor.
|
|
1357
|
+
*/
|
|
1358
|
+
interface ResumePort<S, M extends {
|
|
1359
|
+
type: string;
|
|
1360
|
+
}> {
|
|
1361
|
+
readonly isResumable: (state: S) => boolean;
|
|
1362
|
+
readonly resumeMsg: (now: number) => M;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* After `runtime.ready`, derive the single resume Msg from the rehydrated State
|
|
1366
|
+
* via `port` and dispatch it exactly once — the generalized AgentBoot. On a
|
|
1367
|
+
* fresh (non-rehydrated) machine `port.isResumable` is false and this is a
|
|
1368
|
+
* no-op. Call once right after building the runtime; it standardizes the
|
|
1369
|
+
* hand-written `if (isResumable(state)) await runtime.dispatch(bootMsg(now()))`
|
|
1370
|
+
* every DO-hosted machine otherwise re-derives.
|
|
1371
|
+
*
|
|
1372
|
+
* `await ready` is the single boot gate (issue #45): `getState()` is total only
|
|
1373
|
+
* on the booted `Runtime`, so state cannot be inspected before boot populates
|
|
1374
|
+
* it. `now` is injected (the sole boot clock read) so a test can pin it.
|
|
1375
|
+
*/
|
|
1376
|
+
declare function bootResume<S, M extends {
|
|
1377
|
+
type: string;
|
|
1378
|
+
}, E extends {
|
|
1379
|
+
type: string;
|
|
1380
|
+
} = never>(booting: BootingRuntime<S, M, E>, port: ResumePort<S, M>, now?: () => number): Promise<void>;
|
|
1381
|
+
/**
|
|
1382
|
+
* True iff an agent slice loaded from storage is mid-loop (running + awaiting
|
|
1383
|
+
* tools) — the resumable case `agent_boot` re-fires.
|
|
1384
|
+
*
|
|
1385
|
+
* The resumability question IS "is the run suspended on tools?" — so this
|
|
1386
|
+
* delegates to the agent's own typed `status` channel (issue #49) rather than
|
|
1387
|
+
* re-deriving the private `run.phase` / `conversation.awaiting` shape by hand.
|
|
1388
|
+
* The host no longer leaks the agent's internal slice structure; a change to
|
|
1389
|
+
* `Awaiting` surfaces as a compile error in `status`, not a silent break here.
|
|
1390
|
+
*/
|
|
1391
|
+
declare function agentIsResumable<Stage, P extends string, O extends Record<P, unknown>, R>(state: AgentState<Stage, P, O, R>): boolean;
|
|
1392
|
+
/**
|
|
1393
|
+
* The AGENT specialization of {@link bootResume} (issue #231): after
|
|
1394
|
+
* `runtime.ready`, self-dispatch `agent_boot` iff the rehydrated slice is
|
|
1395
|
+
* resumable, a no-op on a fresh DO. Wires the agent slice's `agentIsResumable`
|
|
1396
|
+
* predicate + the agent-owned `agentBootMsg` typed port (issue #60) into the
|
|
1397
|
+
* generalized helper, so the resume dispatch lives in ONE place — see
|
|
1398
|
+
* `bootResume`'s docblock for the boot-gate / init-purity rationale.
|
|
1399
|
+
*
|
|
1400
|
+
* `now` is injected (the host's only clock read for boot) so a test can pin it.
|
|
1401
|
+
*/
|
|
1402
|
+
declare function autoBoot<Stage, P extends string, O extends Record<P, unknown>, R, E extends {
|
|
1403
|
+
type: string;
|
|
1404
|
+
} = never>(booting: BootingRuntime<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, E>, now?: () => number): Promise<void>;
|
|
1086
1405
|
/**
|
|
1087
1406
|
* RE-EMIT ON ACTIVATION (the #91 wake path). Given a durable carrier whose
|
|
1088
1407
|
* recorder was rehydrated from the persisted ledger events (e.g.
|
|
@@ -1112,182 +1431,6 @@ declare function durableCommandCarrier<R>(inner: DeferredGateway<R>, recorder: P
|
|
|
1112
1431
|
* they restore a suspended run's full in-flight state after a wake.
|
|
1113
1432
|
*/
|
|
1114
1433
|
declare function reissueSurvivingEffects<R>(carrier: DurableCommandCarrier<R>, reissue: (callId: string) => void): readonly string[];
|
|
1115
|
-
/**
|
|
1116
|
-
* A set of SSE sinks plus the plumbing to fan an event out to all of them and
|
|
1117
|
-
* to open a `text/event-stream` Response wired to a fresh sink.
|
|
1118
|
-
*
|
|
1119
|
-
* The consumer:
|
|
1120
|
-
* 1. holds one `SseHub<E>` per DO,
|
|
1121
|
-
* 2. calls `hub.emit(event)` from its Msg→event mapping (driven off
|
|
1122
|
-
* `runtime.observe`), and
|
|
1123
|
-
* 3. returns `hub.open()` from the `/sse` route.
|
|
1124
|
-
*
|
|
1125
|
-
* `register(sink)` exists for tests: subscribe a sink directly and observe the
|
|
1126
|
-
* exact event sequence a live client would receive (a live `text/event-stream`
|
|
1127
|
-
* fights vitest-pool-workers teardown). Deletes the consumer's `sseSinks` set,
|
|
1128
|
-
* `openSse`, `emitSse`, and `onSseEvent`.
|
|
1129
|
-
*/
|
|
1130
|
-
interface SseHub<E> {
|
|
1131
|
-
emit(event: E): void;
|
|
1132
|
-
open(): Response;
|
|
1133
|
-
register(sink: (event: E) => void): () => void;
|
|
1134
|
-
}
|
|
1135
|
-
/** Build an SSE hub for event type `E` (the consumer's domain event shape). */
|
|
1136
|
-
declare function sseHub<E>(): SseHub<E>;
|
|
1137
|
-
/**
|
|
1138
|
-
* Express an {@link SseHub} as a {@link Projection}. The projection's view is
|
|
1139
|
-
* the latest emitted SSE event (or `null` before the first); `apply` maps each
|
|
1140
|
-
* write-model update to an event via `toEvent` and emits it through `hub.emit`,
|
|
1141
|
-
* so the SSE stream is driven by the SAME hub `sseHub()` returns — no API
|
|
1142
|
-
* change for the `/sse` route (`hub.open()`) or any existing caller.
|
|
1143
|
-
*
|
|
1144
|
-
* - `toEvent(msg, model)` returns the SSE event for this update, or `null` to
|
|
1145
|
-
* skip it (the view stays, nothing is emitted).
|
|
1146
|
-
* - `name`/`key` default to `"sse"`/`"main"`; override for a second SSE view.
|
|
1147
|
-
*
|
|
1148
|
-
* @example
|
|
1149
|
-
* const hub = sseHub<MyEvent>();
|
|
1150
|
-
* const registry = projectionRegistry<MyState, MyMsg>();
|
|
1151
|
-
* registry.register(sseProjection(hub, (msg) =>
|
|
1152
|
-
* msg?.type === "turn_done" ? { kind: "turn", ... } : null,
|
|
1153
|
-
* ));
|
|
1154
|
-
* driveProjections(registry, runtime); // SSE is now one projection
|
|
1155
|
-
* // /sse route unchanged:
|
|
1156
|
-
* return hub.open();
|
|
1157
|
-
*/
|
|
1158
|
-
declare function sseProjection<Model, Msg extends {
|
|
1159
|
-
type: string;
|
|
1160
|
-
}, E>(hub: SseHub<E>, toEvent: (msg: Msg | null, model: Model) => E | null, id?: {
|
|
1161
|
-
name?: string;
|
|
1162
|
-
key?: string;
|
|
1163
|
-
}): Projection<Model, Msg, E | null>;
|
|
1164
|
-
/**
|
|
1165
|
-
* Wire an {@link SseHub} to a runtime's semantic {@link AgentEvent} stream (#47).
|
|
1166
|
-
* For each event `type` the runtime can emit, subscribes via `runtime.on` and
|
|
1167
|
-
* pushes `toFrame(event)` at the hub (returning `null` skips that event). The
|
|
1168
|
-
* runtime MUST have been built with `run(machine, { events: agentEvents() })`,
|
|
1169
|
-
* which is what makes `on` deliver the `AgentEvent`s. Returns a cleanup that
|
|
1170
|
-
* detaches every subscription.
|
|
1171
|
-
*
|
|
1172
|
-
* This is the runtime→SSE adapter expressed over the typed event channel: no
|
|
1173
|
-
* `observe` firehose, no private-Msg coupling. `hub.open()` and every existing
|
|
1174
|
-
* `sseHub` caller are unchanged — only the SOURCE of `hub.emit` moves from a
|
|
1175
|
-
* hand-rolled Msg switch to `on`.
|
|
1176
|
-
*
|
|
1177
|
-
* @example
|
|
1178
|
-
* const hub = sseHub<MyFrame>();
|
|
1179
|
-
* const runtime = await run(machine, { ctx, events: agentEvents() }).ready;
|
|
1180
|
-
* const off = sseFromAgentEvents(runtime, hub, (e) =>
|
|
1181
|
-
* e.type === "RunDone" ? { kind: "done", output: e.output } : null,
|
|
1182
|
-
* );
|
|
1183
|
-
* // /sse route unchanged:
|
|
1184
|
-
* return hub.open();
|
|
1185
|
-
*/
|
|
1186
|
-
declare function sseFromAgentEvents<R, Frame>(runtime: {
|
|
1187
|
-
on<K extends AgentEvent<R>["type"]>(type: K, handler: (event: Extract<AgentEvent<R>, {
|
|
1188
|
-
type: K;
|
|
1189
|
-
}>) => void): () => void;
|
|
1190
|
-
}, hub: SseHub<Frame>, toFrame: (event: AgentEvent<R>) => Frame | null): () => void;
|
|
1191
|
-
/**
|
|
1192
|
-
* What the consumer supplies to {@link createAgentHost} — the domain mappings,
|
|
1193
|
-
* nothing of the wiring. `Stage`/`P`/`O`/`R` are the agent slice's type
|
|
1194
|
-
* parameters (the consumer names them once); `Frame` is its SSE event shape.
|
|
1195
|
-
* `C`/`U`/`Ctx` are the machine's own Cmd/Sub/Ctx — inferred from
|
|
1196
|
-
* `buildMachine` at the `createAgentHost` call site (never name them by hand;
|
|
1197
|
-
* an erased `any` here cost consumers all checking on this layer — #278).
|
|
1198
|
-
*/
|
|
1199
|
-
interface AgentHostConfig<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown> {
|
|
1200
|
-
/**
|
|
1201
|
-
* Build the wired agent machine. Called once per host build (per activation),
|
|
1202
|
-
* AFTER the previous runtime — if any — was torn down. The consumer wires its
|
|
1203
|
-
* per-tool interpret here (`agent.toMachine({ toolInterpret })`).
|
|
1204
|
-
*/
|
|
1205
|
-
readonly buildMachine: () => Machine<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, C, U, Ctx>;
|
|
1206
|
-
/** The durable `Store` for the agent slice (typically `doStore(storage, parse)`). */
|
|
1207
|
-
readonly store: Store<AgentState<Stage, P, O, R>>;
|
|
1208
|
-
/** The Ctx the machine threads to its interpret cells — checked against `buildMachine`'s. */
|
|
1209
|
-
readonly ctx: Ctx;
|
|
1210
|
-
/**
|
|
1211
|
-
* The run-terminality predicate (#46) — what makes `result()` first-class.
|
|
1212
|
-
* Defaults to the agent's own terminal phases (`done` / `failed`).
|
|
1213
|
-
*/
|
|
1214
|
-
readonly terminal?: (state: AgentState<Stage, P, O, R>) => boolean;
|
|
1215
|
-
/**
|
|
1216
|
-
* Map one semantic {@link AgentEvent} to the consumer's SSE frame, or `null`
|
|
1217
|
-
* to skip it. This is the ONLY place the consumer touches the SSE seam — the
|
|
1218
|
-
* host owns the subscription wiring (`sseFromAgentEvents`) and the hub.
|
|
1219
|
-
*/
|
|
1220
|
-
readonly toSseFrame: (event: AgentEvent<R>) => Frame | null;
|
|
1221
|
-
/** Clock injected for `autoBoot` (the host's only boot clock read). */
|
|
1222
|
-
readonly now?: () => number;
|
|
1223
|
-
}
|
|
1224
|
-
/**
|
|
1225
|
-
* The assembled host: the runtime cell + the SSE hub + the framework test seam,
|
|
1226
|
-
* owned ONCE. A DO holds one `AgentHost` and delegates to it; its own surface
|
|
1227
|
-
* shrinks to the domain (the gateway, the WS bridge, the command-send mapping).
|
|
1228
|
-
*
|
|
1229
|
-
* @typeParam Stage Pipeline stage type.
|
|
1230
|
-
* @typeParam P Brain-call purposes.
|
|
1231
|
-
* @typeParam O Per-purpose outputs (bound to {@link AgentTurn}).
|
|
1232
|
-
* @typeParam R Tool result type.
|
|
1233
|
-
* @typeParam Frame The consumer's SSE event shape.
|
|
1234
|
-
*/
|
|
1235
|
-
interface AgentHost<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame> {
|
|
1236
|
-
/**
|
|
1237
|
-
* Get (or build) the booted runtime. Build-once-boot-once: the first call
|
|
1238
|
-
* builds the machine, wires SSE off the semantic event stream, awaits the boot
|
|
1239
|
-
* gate, runs the `autoBoot` re-fire for a rehydrated suspended run, and caches
|
|
1240
|
-
* the booted `Runtime`; subsequent calls return the cached handle. The single
|
|
1241
|
-
* assembly every consumer used to hand-roll in `getRuntime()`.
|
|
1242
|
-
*/
|
|
1243
|
-
runtime(): Promise<Runtime<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R>, AgentEvent<R>>>;
|
|
1244
|
-
/**
|
|
1245
|
-
* The agent's lifecycle status (#49) — the ONE typed channel a consumer reads
|
|
1246
|
-
* instead of re-deriving `run.phase` / `awaiting` by hand. Ensures the runtime
|
|
1247
|
-
* is built + boot-reconciled first. Replaces the per-consumer `isSuspended` /
|
|
1248
|
-
* `runPhase` test methods (read `status().kind`).
|
|
1249
|
-
*/
|
|
1250
|
-
status(): Promise<AgentStatus<Stage>>;
|
|
1251
|
-
/**
|
|
1252
|
-
* The run's terminal State (#46), or `undefined` while in flight. Read the
|
|
1253
|
-
* run's product off it (e.g. `result()?.output`). Replaces the per-consumer
|
|
1254
|
-
* `verdict` plumbing's source. Ensures the runtime is built first.
|
|
1255
|
-
*/
|
|
1256
|
-
result(): Promise<AgentState<Stage, P, O, R> | undefined>;
|
|
1257
|
-
/**
|
|
1258
|
-
* The SSE hub — the route (`host.sse.open()`) AND the test seam
|
|
1259
|
-
* (`host.sse.register(sink)`). The host drives `hub.emit` off the semantic
|
|
1260
|
-
* event stream; the consumer never touches it except to open / register.
|
|
1261
|
-
*/
|
|
1262
|
-
readonly sse: SseHub<Frame>;
|
|
1263
|
-
/**
|
|
1264
|
-
* Tear down the cached runtime (drain its tail via `stop()`, drop the handle)
|
|
1265
|
-
* so the next `runtime()` rebuilds from storage. The framework `settle` test
|
|
1266
|
-
* seam, owned once.
|
|
1267
|
-
*/
|
|
1268
|
-
reset(): Promise<void>;
|
|
1269
|
-
}
|
|
1270
|
-
/**
|
|
1271
|
-
* Build an {@link AgentHost} from the consumer's domain mappings. Owns the
|
|
1272
|
-
* runtime+events+autoBoot assembly and the framework test seam — the wiring
|
|
1273
|
-
* issue #53 had every consumer re-write.
|
|
1274
|
-
*
|
|
1275
|
-
* @example
|
|
1276
|
-
* const host = createAgentHost<Stage, Purpose, Outputs, ClientResult, SseEvent>({
|
|
1277
|
-
* buildMachine: () => agent.toMachine<Ctx>({ toolInterpret }),
|
|
1278
|
-
* store: doStore(storage, parse),
|
|
1279
|
-
* ctx,
|
|
1280
|
-
* toSseFrame: (e) =>
|
|
1281
|
-
* e.type === "ToolSettled" ? { kind: "result", ...} :
|
|
1282
|
-
* e.type === "TurnSettled" && e.turn.toolCalls.length === 0
|
|
1283
|
-
* ? { kind: "verdict", verdict: e.turn.content } :
|
|
1284
|
-
* e.type === "RunDone" ? { kind: "phase", phase: "done" } : null,
|
|
1285
|
-
* });
|
|
1286
|
-
* // route: return host.sse.open();
|
|
1287
|
-
* // start: await (await host.runtime()).dispatch({ type: "agent_start", ... });
|
|
1288
|
-
* // test: (await host.status()).kind === "suspended"
|
|
1289
|
-
*/
|
|
1290
|
-
declare function createAgentHost<Stage, P extends string, O extends Record<P, AgentTurn>, R, Frame, C extends Cmd = Cmd, U extends Sub = Sub, Ctx = unknown>(config: AgentHostConfig<Stage, P, O, R, Frame, C, U, Ctx>): AgentHost<Stage, P, O, R, Frame>;
|
|
1291
1434
|
|
|
1292
1435
|
/**
|
|
1293
1436
|
* @demlik/tea/do — the idempotent-effect brick (the receiver/effect-side dedup).
|
|
@@ -1668,6 +1811,7 @@ declare function acceptPresenceSocket<A>(ctx: PresenceCtx, options?: RegisterOpt
|
|
|
1668
1811
|
declare function presenceCount(ctx: PresenceCtx, tag?: string): number;
|
|
1669
1812
|
|
|
1670
1813
|
/**
|
|
1814
|
+
* @packageDocumentation
|
|
1671
1815
|
* @demlik/tea/do — Durable Object adapter for `@demlik/tea`.
|
|
1672
1816
|
*
|
|
1673
1817
|
* The integration is built on ONE transport model and one durability primitive:
|
|
@@ -1763,4 +1907,4 @@ interface DoStoreOptions<S> {
|
|
|
1763
1907
|
*/
|
|
1764
1908
|
declare function doStore<S>(storage: DurableObjectStorage, parse: (raw: unknown) => S | null, keyOrOptions?: string | DoStoreOptions<S>): Store<S>;
|
|
1765
1909
|
|
|
1766
|
-
export { type AgentHost, type AgentHostConfig, type AlarmStorage, type AppliedEffects, type AppliedEffectsEvent, type AppliedEffectsGuard, type AttachableSocket, type BroadcastOptions, type BroadcastReport, type DeferredGateway, type DoStoreOptions, type DurableCommandCarrier, type DurableTimer, type DurableTimerConfig, type EffectApplied, EffectConfirmed, type EffectForgotten, type EffectKey, EffectOwed, type EventLogRange, type EventSourcedOptions, type EventSourcedStore, type ExecuteStep, type HibernatableCtx, type IdempotentEffect, type IdempotentOutcome, type NextStep, PendingEffectsRecorder, type PersistedEvent, type PresenceCtx, type PresenceSocket, type PresenceUpgrade, type Projection, type ProjectionId, type ProjectionRegistry, type ProjectionRunner, type ProjectionUpdate, type RegisterOptions, type ResumePort, type RunStepLoopConfig, type SseHub, type StepCtx, type StepEngine, type StepHostConfig, type StepLoopOutcome, type StepOutcome, type StepRequest, type StepResponse, type StepResult, type StepTransport, WS_READY_STATE_OPEN, acceptCommandSocket, acceptDurableCommandSocket, acceptPresenceSocket, agentIsResumable, appliedEffects, applyAppliedEvent, autoBoot, bootResume, broadcast, broadcastFrame, broadcastHibernatable, constantTimeEqual, createAgentHost, deferredGateway, doEventSourcedStore, doStore, driveProjections, durableCommandCarrier, durableDeferredGateway, durableTimer, emptyApplied, foldApplied, idempotentEffect, isApplied, mintRunToken, presenceCount, projectionIdString, projectionRegistry, rebuildProjection, registerHibernatableSocket, reissueSurvivingEffects, runProjection, runStepLoop, sseFromAgentEvents, sseHub, sseProjection, stepHost };
|
|
1910
|
+
export { type AgentHost, type AgentHostConfig, type AlarmStorage, type AppliedEffects, type AppliedEffectsEvent, type AppliedEffectsGuard, type AttachableSocket, type BroadcastOptions, type BroadcastReport, type DeferResumeHook, type DeferStepHostConfig, type DeferredGateway, type DeferredStepOutcome, type DeferredStepResponse, type DoStoreOptions, type DurableCommandCarrier, type DurableTimer, type DurableTimerConfig, type EffectApplied, EffectConfirmed, type EffectForgotten, type EffectKey, EffectOwed, type EventLogRange, type EventSourcedOptions, type EventSourcedStore, type ExecuteStep, type HibernatableCtx, type IdempotentEffect, type IdempotentOutcome, type NextStep, PendingEffectsRecorder, type PersistedEvent, type PresenceCtx, type PresenceSocket, type PresenceUpgrade, type Projection, type ProjectionErrorContext, type ProjectionId, type ProjectionOnError, type ProjectionRegistry, type ProjectionRunner, type ProjectionUpdate, type RegisterOptions, type ResumePort, type RunStepLoopConfig, type SseHub, type StepCtx, type StepEngine, type StepHostConfig, type StepLoopOutcome, type StepOutcome, type StepRequest, type StepResponse, type StepResult, type StepTransport, type StepWorking, WS_READY_STATE_OPEN, acceptCommandSocket, acceptDurableCommandSocket, acceptPresenceSocket, agentIsResumable, appliedEffects, applyAppliedEvent, autoBoot, bootResume, broadcast, broadcastFrame, broadcastHibernatable, constantTimeEqual, createAgentHost, deferredGateway, doEventSourcedStore, doStore, driveProjections, durableCommandCarrier, durableDeferredGateway, durableTimer, emptyApplied, foldApplied, idempotentEffect, isApplied, mintRunToken, presenceCount, projectionIdString, projectionRegistry, rebuildProjection, registerHibernatableSocket, reissueSurvivingEffects, runProjection, runStepLoop, sseFromAgentEvents, sseHub, sseProjection, stepHost };
|