@demlik/tea 0.20.0 → 0.22.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.
Files changed (130) hide show
  1. package/README.md +11 -2
  2. package/dist/agent/index.d.ts +36 -23
  3. package/dist/agent/index.js +12 -10
  4. package/dist/{chunk-UHAQ4PI6.js → chunk-2A5XTKQQ.js} +205 -59
  5. package/dist/chunk-2A5XTKQQ.js.map +1 -0
  6. package/dist/{chunk-JAKM53JF.js → chunk-7K7SEYCH.js} +16 -15
  7. package/dist/chunk-7K7SEYCH.js.map +1 -0
  8. package/dist/{chunk-RKVUVALV.js → chunk-A3QPMX5M.js} +31 -9
  9. package/dist/chunk-A3QPMX5M.js.map +1 -0
  10. package/dist/{chunk-DVNXEB7V.js → chunk-BVFS3ICC.js} +3 -3
  11. package/dist/chunk-BVFS3ICC.js.map +1 -0
  12. package/dist/{chunk-FTCYY2DO.js → chunk-CPE5ZQXQ.js} +3 -3
  13. package/dist/{chunk-FTCYY2DO.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
  14. package/dist/chunk-DEAPNMKO.js +11 -0
  15. package/dist/chunk-DEAPNMKO.js.map +1 -0
  16. package/dist/{chunk-XWIE5V7R.js → chunk-ETRNSJCR.js} +4 -4
  17. package/dist/{chunk-XWIE5V7R.js.map → chunk-ETRNSJCR.js.map} +1 -1
  18. package/dist/chunk-FXWDDNGD.js +98 -0
  19. package/dist/chunk-FXWDDNGD.js.map +1 -0
  20. package/dist/chunk-GFGOQIEC.js +10 -0
  21. package/dist/chunk-GFGOQIEC.js.map +1 -0
  22. package/dist/{chunk-2R62Q5UK.js → chunk-IAHEYPYJ.js} +3 -3
  23. package/dist/{chunk-2R62Q5UK.js.map → chunk-IAHEYPYJ.js.map} +1 -1
  24. package/dist/{chunk-NGYFBD5G.js → chunk-J5JNLXO7.js} +3 -3
  25. package/dist/{chunk-NGYFBD5G.js.map → chunk-J5JNLXO7.js.map} +1 -1
  26. package/dist/{chunk-72CN3VPY.js → chunk-NFOGYDRS.js} +5 -5
  27. package/dist/{chunk-72CN3VPY.js.map → chunk-NFOGYDRS.js.map} +1 -1
  28. package/dist/{chunk-A73IJ27X.js → chunk-R2YNDRGL.js} +3 -3
  29. package/dist/{chunk-A73IJ27X.js.map → chunk-R2YNDRGL.js.map} +1 -1
  30. package/dist/{chunk-5HVKDHY6.js → chunk-R5NXOPL2.js} +3 -3
  31. package/dist/{chunk-5HVKDHY6.js.map → chunk-R5NXOPL2.js.map} +1 -1
  32. package/dist/chunk-RMBZ7YH7.js +52 -0
  33. package/dist/chunk-RMBZ7YH7.js.map +1 -0
  34. package/dist/{chunk-AIXC33SE.js → chunk-SHXUSTHM.js} +62 -15
  35. package/dist/chunk-SHXUSTHM.js.map +1 -0
  36. package/dist/{chunk-TAKPC6SD.js → chunk-UGTC33VH.js} +4 -4
  37. package/dist/{chunk-TAKPC6SD.js.map → chunk-UGTC33VH.js.map} +1 -1
  38. package/dist/{chunk-EA47MI5Q.js → chunk-VH5MYJ2K.js} +4 -4
  39. package/dist/{chunk-EA47MI5Q.js.map → chunk-VH5MYJ2K.js.map} +1 -1
  40. package/dist/{chunk-2774I242.js → chunk-WZEDIUWR.js} +21 -20
  41. package/dist/chunk-WZEDIUWR.js.map +1 -0
  42. package/dist/{chunk-TGJLXZAU.js → chunk-ZRRET3RP.js} +11 -11
  43. package/dist/chunk-ZRRET3RP.js.map +1 -0
  44. package/dist/{core-CtvAyPpH.d.ts → core-DNDAEhO2.d.ts} +9 -12
  45. package/dist/devtools/index.d.ts +4 -4
  46. package/dist/do/index.d.ts +103 -38
  47. package/dist/do/index.js +40 -21
  48. package/dist/do/index.js.map +1 -1
  49. package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
  50. package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
  51. package/dist/effect/index.d.ts +121 -28
  52. package/dist/effect/index.js +45 -31
  53. package/dist/effect/index.js.map +1 -1
  54. package/dist/extension/index.d.ts +3 -3
  55. package/dist/extension/index.js +2 -2
  56. package/dist/extension/index.js.map +1 -1
  57. package/dist/flow/index.d.ts +17 -13
  58. package/dist/flow/index.js +15 -12
  59. package/dist/flow/index.js.map +1 -1
  60. package/dist/idempotency/index.d.ts +3 -3
  61. package/dist/idempotency/index.js +3 -3
  62. package/dist/{index-CrcoVfd_.d.ts → index-9P1PaXIV.d.ts} +2 -2
  63. package/dist/{index-D3NY3VCN.d.ts → index-BBLI2WWN.d.ts} +1 -1
  64. package/dist/{index-Dl71V7kt.d.ts → index-C1WhBZ6a.d.ts} +4 -5
  65. package/dist/{index-CKtEKGyp.d.ts → index-C92SgNWz.d.ts} +3 -3
  66. package/dist/{index-CZZ3HI-Z.d.ts → index-CAsUE_d1.d.ts} +4 -4
  67. package/dist/{index-EIgfl83J.d.ts → index-CO34esWT.d.ts} +2 -2
  68. package/dist/{index-Bk2hBUdK.d.ts → index-COeX9Bmz.d.ts} +11 -9
  69. package/dist/{index-CVU7SK3a.d.ts → index-Cg0QXnzf.d.ts} +2 -2
  70. package/dist/index.d.ts +9 -9
  71. package/dist/index.js +2 -2
  72. package/dist/jev/index.d.ts +15 -15
  73. package/dist/jev/index.js +5 -5
  74. package/dist/jev/index.js.map +1 -1
  75. package/dist/laws-DTtCDDlU.d.ts +135 -0
  76. package/dist/loop-DReKGV1X.d.ts +70 -0
  77. package/dist/{machine-Dtre2TmN.d.ts → machine-C0WhOAyB.d.ts} +7 -7
  78. package/dist/machine-viz/index.d.ts +1 -1
  79. package/dist/machine-viz/index.js +2 -2
  80. package/dist/mem/index.d.ts +4 -3
  81. package/dist/mem/index.js +2 -2
  82. package/dist/mem/index.js.map +1 -1
  83. package/dist/node/index.d.ts +57 -9
  84. package/dist/node/index.js +175 -68
  85. package/dist/node/index.js.map +1 -1
  86. package/dist/otel/index.d.ts +28 -12
  87. package/dist/otel/index.js +25 -21
  88. package/dist/otel/index.js.map +1 -1
  89. package/dist/paginate/index.d.ts +4 -4
  90. package/dist/paginate/index.js +5 -5
  91. package/dist/parity/index.d.ts +11 -15
  92. package/dist/parity/index.js +4 -9
  93. package/dist/parity/index.js.map +1 -1
  94. package/dist/pbt/index.d.ts +279 -67
  95. package/dist/pbt/index.js +299 -58
  96. package/dist/pbt/index.js.map +1 -1
  97. package/dist/persistence/index.d.ts +10 -12
  98. package/dist/persistence/index.js +4 -4
  99. package/dist/promise/index.d.ts +118 -21
  100. package/dist/promise/index.js +6 -4
  101. package/dist/react/index.d.ts +34 -21
  102. package/dist/react/index.js +271 -42
  103. package/dist/react/index.js.map +1 -1
  104. package/dist/resilience/index.d.ts +6 -6
  105. package/dist/resilience/index.js +5 -5
  106. package/dist/{run-fiBe7pOG.d.ts → run-Cnw4qXj2.d.ts} +105 -23
  107. package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-CP-ykwgf.d.ts} +157 -29
  108. package/dist/testing/effect/index.d.ts +162 -9
  109. package/dist/testing/effect/index.js +110 -10
  110. package/dist/testing/effect/index.js.map +1 -1
  111. package/dist/testing/index.d.ts +6 -8
  112. package/dist/testing/index.js +4 -24
  113. package/dist/testing/index.js.map +1 -1
  114. package/dist/testing/promise/index.d.ts +4 -4
  115. package/dist/testing/promise/index.js +5 -5
  116. package/dist/timing/index.d.ts +4 -4
  117. package/dist/timing/index.js +4 -4
  118. package/dist/{types-CXNzlbAg.d.ts → types-S53WDy_h.d.ts} +1 -1
  119. package/dist/work-queue/index.d.ts +4 -4
  120. package/dist/work-queue/index.js +2 -2
  121. package/package.json +19 -17
  122. package/dist/chunk-2774I242.js.map +0 -1
  123. package/dist/chunk-AIXC33SE.js.map +0 -1
  124. package/dist/chunk-DVNXEB7V.js.map +0 -1
  125. package/dist/chunk-HX3C6LDK.js +0 -34
  126. package/dist/chunk-HX3C6LDK.js.map +0 -1
  127. package/dist/chunk-JAKM53JF.js.map +0 -1
  128. package/dist/chunk-RKVUVALV.js.map +0 -1
  129. package/dist/chunk-TGJLXZAU.js.map +0 -1
  130. package/dist/chunk-UHAQ4PI6.js.map +0 -1
package/README.md CHANGED
@@ -10,8 +10,8 @@ pnpm add @demlik/tea
10
10
 
11
11
  The one runtime dependency is `@standard-schema/spec`, a types-only package
12
12
  installed for you. [`Cmd.define`](https://github.com/kamp-us/demlik/blob/main/packages/tea/docs/glossary.md#cmd) takes any Standard Schema, so bring the schema
13
- library you already use (zod, or Effect Schema through
14
- `Schema.toStandardSchemaV1`). The host and
13
+ library you already use (zod as it is, or Effect Schema through the
14
+ `Cmd.define` that `@demlik/tea/effect` exports). The host and
15
15
  testing adapters ride on optional peers you add only for the surface you use:
16
16
  `react` / `react-dom` (the `./react` hooks), `ws` (Node WebSocket subs),
17
17
  `fast-check` (the `./pbt` property-testing helpers), `vitest` (the testing
@@ -70,6 +70,15 @@ Coming from 0.15?
70
70
  [The migration guide](https://github.com/kamp-us/demlik/blob/main/packages/tea/docs/how-to/migrate-from-0-15.md)
71
71
  shows every removed or reshaped API, before and after.
72
72
 
73
+ ## Durability
74
+
75
+ With `fileStore` or `doStore`, state is saved before commands run. Restarting loads it.
76
+ `agent.run` resumes pending work automatically; custom machines handle a boot
77
+ message. An effect can run again if its result was not saved before the crash.
78
+
79
+ [Make a machine durable](https://github.com/kamp-us/demlik/blob/main/packages/tea/docs/how-to/make-durable.md)
80
+ and [understand restore versus replay](https://github.com/kamp-us/demlik/blob/main/packages/tea/docs/explanation/durability-model.md).
81
+
73
82
  ## Documentation
74
83
 
75
84
  The four Diátaxis quadrants live in [`docs/`](https://github.com/kamp-us/demlik/blob/main/packages/tea/docs/README.md):
@@ -1,29 +1,29 @@
1
- import { M as Machine, I as Interpret, c as Subscribe, H as HandlerCtx, C as Cmd } from '../core-CtvAyPpH.js';
2
- import { b as DeadlinesSub } from '../index-CVU7SK3a.js';
3
- export { a as DeadlineSub, m as deadlineSub, o as deadlinesSub, s as subscribeDeadline } from '../index-CVU7SK3a.js';
4
- import { M as MessageContent, T as ToolCall, a as ToolOutcome, C as ContentPart, b as Conversation, c as AnyToolDef, d as ToolsCtx, e as AgentState, f as AgentTurn, g as ToolResult, A as AgentEvent, h as TurnChunk, i as AgentMachineMsg, W as WiredToolMsg, j as AgentCmd, k as ToolCmd, P as PlainModel, S as StreamingModel, l as ToolFailureOf, m as AgentTerminalFailure, N as NotReserved, n as ToolDef, o as ToolThrown, p as AgentEndedStatus, q as AgentConfigCore, r as CompactionPolicy, s as AgentKnob } from '../machine-Dtre2TmN.js';
5
- export { t as AGENT_EVENT_TYPES, u as AgentBootMsg, v as AgentCancelMsg, w as AgentCompactErrMsg, x as AgentCompactOkMsg, y as AgentCompactRunCmd, z as AgentCompactionConfig, B as AgentConfig, D as AgentEventHead, E as AgentFailure, F as AgentLifecycleNote, G as AgentLlmErrMsg, H as AgentLlmOkMsg, I as AgentLlmRunCmd, J as AgentSnapshotConfig, K as AgentStatus, L as AgentTimerMsg, O as AgentToMachine, Q as Awaiting, R as COMPACTION_PURPOSE, U as CompactInterpret, V as CompactionOutputs, X as CompactionPurpose, Y as CompactionSummary, Z as FILE_OMITTED, _ as FilePart, $ as IMAGE_OMITTED, a0 as ImagePart, a1 as Llm, a2 as LlmCall, a3 as LlmErr, a4 as LlmFailMsg, a5 as LlmOk, a6 as LlmRunCmd, a7 as LlmSucceedMsg, a8 as MediaSource, a9 as MessageLoader, aa as ModelFactory, ab as ModelPort, ac as ModelStream, ad as PLAIN_MODEL_MISROUTE_REASON, ae as ReservedToolName, af as Schema, ag as SnapshotInterpret, ah as TOOL_RETRY_EXHAUSTED_TAG, ai as TOOL_TIMEOUT_TAG, aj as TaggedFailure, ak as TextPart, al as ToolConstructors, am as ToolError, an as ToolFail, ao as ToolFailure, ap as ToolHandler, aq as ToolInput, ar as ToolMsg, as as ToolOk, at as ToolRecord, au as ToolRejectedCmd, av as ToolRejection, aw as ToolResilience, ax as ToolResilienceError, ay as ToolRetryExhausted, az as ToolRouter, aA as ToolSettlement, aB as ToolTimedOut, aC as TurnUsage, aD as WiredToolCmd, aE as agentBootMsg, aF as agentCancelMsg, aG as agentEvents, aH as agentTurnSchema, aI as compactionSummarySchema, aJ as contentParts, aK as fanOutInterpret, aL as isAgentTurn, aM as isCompactionSummary, aN as isReservedToolName, aO as isStreamingModel, aP as isTurnUsage, aQ as liftAgent, aR as mergeInterpret, aS as omitMedia, aT as plainModel, aU as status, aV as tool, aW as toolErrorReason, aX as toolRouter } from '../machine-Dtre2TmN.js';
1
+ import { M as Machine, I as Interpret, S as Subscribe, H as HandlerCtx, C as Cmd } from '../core-DNDAEhO2.js';
2
+ import { b as DeadlinesSub } from '../index-Cg0QXnzf.js';
3
+ export { a as DeadlineSub, m as deadlineSub, o as deadlinesSub, s as subscribeDeadline } from '../index-Cg0QXnzf.js';
4
+ import { M as MessageContent, T as ToolCall, a as ToolOutcome, C as ContentPart, b as Conversation, c as AnyToolDef, d as ToolsCtx, e as AgentState, f as AgentTurn, g as ToolResult, A as AgentEvent, h as TurnChunk, i as AgentMachineMsg, W as WiredToolMsg, j as AgentCmd, k as ToolCmd, P as PlainModel, S as StreamingModel, l as ToolFailureOf, m as AgentTerminalFailure, N as NotReserved, n as ToolDef, o as ToolThrown, p as AgentEndedStatus, q as AgentConfigCore, r as CompactionPolicy, s as AgentKnob } from '../machine-C0WhOAyB.js';
5
+ export { t as AGENT_EVENT_TYPES, u as AgentBootMsg, v as AgentCancelMsg, w as AgentCompactErrMsg, x as AgentCompactOkMsg, y as AgentCompactRunCmd, z as AgentCompactionConfig, B as AgentConfig, D as AgentEventHead, E as AgentFailure, F as AgentLifecycleNote, G as AgentLlmErrMsg, H as AgentLlmOkMsg, I as AgentLlmRunCmd, J as AgentSnapshotConfig, K as AgentStatus, L as AgentTimerMsg, O as AgentToMachine, Q as Awaiting, R as COMPACTION_PURPOSE, U as CompactInterpret, V as CompactionOutputs, X as CompactionPurpose, Y as CompactionSummary, Z as FILE_OMITTED, _ as FilePart, $ as IMAGE_OMITTED, a0 as ImagePart, a1 as Llm, a2 as LlmCall, a3 as LlmErr, a4 as LlmFailMsg, a5 as LlmOk, a6 as LlmRunCmd, a7 as LlmSucceedMsg, a8 as MediaSource, a9 as MessageLoader, aa as ModelFactory, ab as ModelPort, ac as ModelStream, ad as PLAIN_MODEL_MISROUTE_REASON, ae as ReservedToolName, af as Schema, ag as SnapshotInterpret, ah as TOOL_RETRY_EXHAUSTED_TAG, ai as TOOL_TIMEOUT_TAG, aj as TaggedFailure, ak as TextPart, al as ToolConstructors, am as ToolError, an as ToolFail, ao as ToolFailure, ap as ToolHandler, aq as ToolInput, ar as ToolMsg, as as ToolOk, at as ToolRecord, au as ToolRejectedCmd, av as ToolRejection, aw as ToolResilience, ax as ToolResilienceError, ay as ToolRetryExhausted, az as ToolRouter, aA as ToolSettlement, aB as ToolTimedOut, aC as TurnUsage, aD as WiredToolCmd, aE as agentBootMsg, aF as agentCancelMsg, aG as agentEvents, aH as agentTurnSchema, aI as compactionSummarySchema, aJ as contentParts, aK as fanOutInterpret, aL as isAgentTurn, aM as isCompactionSummary, aN as isReservedToolName, aO as isStreamingModel, aP as isTurnUsage, aQ as liftAgent, aR as mergeInterpret, aS as omitMedia, aT as plainModel, aU as status, aV as tool, aW as toolErrorReason, aX as toolRouter } from '../machine-C0WhOAyB.js';
6
6
  import { StandardSchemaV1 } from '@standard-schema/spec';
7
- import { C as CtxArg, S as Store } from '../runtime-types-DEluQgGO.js';
8
- import { E as EndedRun } from '../index-CZZ3HI-Z.js';
9
- export { M as MonitoredRunCmd, R as RunFailure } from '../index-CZZ3HI-Z.js';
7
+ import { C as CtxArg, S as StoreAndOnError, O as OnError, U as UnfencedStore, b as Store } from '../runtime-types-CP-ykwgf.js';
8
+ import { E as EndedRun } from '../index-CAsUE_d1.js';
9
+ export { M as MonitoredRunCmd, R as RunFailure } from '../index-CAsUE_d1.js';
10
10
  import { RetryPolicy } from '../retry-backoff/index.js';
11
- import '../types-CXNzlbAg.js';
12
- import '../index-CKtEKGyp.js';
11
+ import '../types-S53WDy_h.js';
12
+ import '../index-C92SgNWz.js';
13
13
  import '../protocol-ClRZBhHQ.js';
14
- import '../index-CrcoVfd_.js';
15
- import '../index-D3NY3VCN.js';
16
- import '../index-Bk2hBUdK.js';
14
+ import '../index-9P1PaXIV.js';
15
+ import '../index-BBLI2WWN.js';
16
+ import '../index-COeX9Bmz.js';
17
17
 
18
18
  /**
19
19
  * @demlik/tea/agent — `defineAgent`, the convenience layer over `createAgent`.
20
20
  *
21
- * `defineAgent({ model, tools, instructions })` is the three-line door: it
21
+ * `defineAgent({ model, tools, instructions })` is the three-line entry: it
22
22
  * defaults the single-stage wiring (`stages`, `turnOf`, `schemas`), derives the
23
23
  * tool handlers from `toolRouter`, renders the prompt off the Model, and absorbs
24
24
  * the drive loop through `driveToDone`. It hides wiring, never state (ADR
25
25
  * 0015): the Model it runs is the same `AgentState` a hand-wired `createAgent`
26
- * produces, under the same keys, and `machine(input)` is the door down to the
26
+ * produces, under the same keys, and `machine(input)` is the way down to the
27
27
  * raw kernel `run`. Each hidden thing is a named helper below.
28
28
  */
29
29
 
@@ -408,9 +408,11 @@ type DefinedAgentEvent<T extends AnyToolDef> = AgentEvent<ToolResult<T>>;
408
408
  *
409
409
  * `ctx` is the plain object the tools' handlers read (ADR 0020). A host with a
410
410
  * resource to release wraps `run` in its own `try` / `finally`.
411
+ *
412
+ * `onError` is the run's sink, and a fenced `store` needs one (see
413
+ * {@link StoreAndOnError}).
411
414
  */
412
- type DefinedAgentRunOptions<T extends AnyToolDef> = CtxArg<DefinedAgentCtx<T>> & {
413
- readonly store?: Store<DefinedAgentState<T>>;
415
+ type DefinedAgentRunOptions<T extends AnyToolDef> = CtxArg<DefinedAgentCtx<T>> & StoreAndOnError<DefinedAgentState<T>> & {
414
416
  /** The run's identity. Omit → a fresh UUID. */
415
417
  readonly runId?: string;
416
418
  /** The clock that stamps `at`. Omit → `Date.now`. */
@@ -524,7 +526,7 @@ interface DefinedAgent<T extends AnyToolDef> {
524
526
  readonly run: (input: string, ...opts: [Record<never, never>] extends [DefinedAgentCtx<T>] ? [opts?: DefinedAgentRunOptions<T>] : [opts: DefinedAgentRunOptions<T>]) => Promise<DefinedAgentResolvedState<T>>;
525
527
  /**
526
528
  * The machine `run` drives for `input` and the interpret table it runs under
527
- * — the door down to the raw kernel.
529
+ * — the way down to the raw kernel.
528
530
  */
529
531
  readonly machine: (input: string) => DefinedAgentWired<T>;
530
532
  /**
@@ -546,7 +548,7 @@ interface DefinedAgent<T extends AnyToolDef> {
546
548
  * The wrapped cell settles through the SAME typed Cmd→Msg edge as the cell it
547
549
  * wraps — it returns whatever `next` returned — so the reducer folds the same
548
550
  * Msgs and a replay of a wrapped run is the unwrapped run's replay. That is
549
- * the whole contract: the door is one over the effect boundary, never over
551
+ * the whole contract: the wrapper is one over the effect boundary, never over
550
552
  * the fold. A cell that must settle DIFFERENTLY is a different machine, and
551
553
  * `createAgent` is still where you build one.
552
554
  *
@@ -654,12 +656,23 @@ type AgentToolSpec<Args, Ok, CT extends AnyToolDef, Ctx> = {
654
656
  */
655
657
  readonly namespace: (ctx: HandlerCtx<Ctx>) => string;
656
658
  /**
657
- * The child run's Store at `key`. The same key must reach the same durable
658
- * cell on every call, in every process — that is what makes a re-fired call
659
- * resume the child instead of restarting it.
659
+ * What the child run's failures with no caller to reject at reach. See
660
+ * `run`'s `onError`.
660
661
  */
662
+ readonly onError?: OnError;
663
+ } & ChildCtxOption<CT, Ctx> & ChildStoreOption<CT, Ctx>;
664
+ /**
665
+ * The child run's Store at `key`. The same key must reach the same durable cell
666
+ * on every call, in every process — that is what makes a re-fired call resume
667
+ * the child instead of restarting it. A child on a fenced Store needs an
668
+ * `onError`, the same rule `run`'s own `store` follows (ADR 0031).
669
+ */
670
+ type ChildStoreOption<CT extends AnyToolDef, Ctx> = {
671
+ readonly store: (key: string, ctx: HandlerCtx<Ctx>) => UnfencedStore<DefinedAgentState<CT>>;
672
+ } | {
661
673
  readonly store: (key: string, ctx: HandlerCtx<Ctx>) => Store<DefinedAgentState<CT>>;
662
- } & ChildCtxOption<CT, Ctx>;
674
+ readonly onError: OnError;
675
+ };
663
676
  /**
664
677
  * Wrap a child `defineAgent` as a tool a parent `defineAgent` calls and awaits
665
678
  * — pass it the child, how to phrase its input and how to read its answer, and
@@ -1,16 +1,18 @@
1
- export { AGENT_EVENT_TYPES, COMPACTION_PURPOSE, FILE_OMITTED, IMAGE_OMITTED, PLAIN_MODEL_MISROUTE_REASON, TOOL_RETRY_EXHAUSTED_TAG, TOOL_TIMEOUT_TAG, agentBootMsg, agentCancelMsg, agentEvents, agentTool, agentTurnSchema, compactionSummarySchema, contentParts, createAgent, defineAgent, fanOutInterpret, isAgentTurn, isCompactionSummary, isReservedToolName, isStreamingModel, isTurnUsage, liftAgent, mergeInterpret, omitMedia, plainModel, renderPrompt, status, tool, toolErrorReason, toolRouter, transcript } from '../chunk-2774I242.js';
2
- import '../chunk-EA47MI5Q.js';
3
- import '../chunk-2R62Q5UK.js';
1
+ export { AGENT_EVENT_TYPES, COMPACTION_PURPOSE, FILE_OMITTED, IMAGE_OMITTED, PLAIN_MODEL_MISROUTE_REASON, TOOL_RETRY_EXHAUSTED_TAG, TOOL_TIMEOUT_TAG, agentBootMsg, agentCancelMsg, agentEvents, agentTool, agentTurnSchema, compactionSummarySchema, contentParts, createAgent, defineAgent, fanOutInterpret, isAgentTurn, isCompactionSummary, isReservedToolName, isStreamingModel, isTurnUsage, liftAgent, mergeInterpret, omitMedia, plainModel, renderPrompt, status, tool, toolErrorReason, toolRouter, transcript } from '../chunk-WZEDIUWR.js';
2
+ import '../chunk-VH5MYJ2K.js';
3
+ import '../chunk-IAHEYPYJ.js';
4
4
  import '../chunk-QNGYQMZB.js';
5
- import '../chunk-72CN3VPY.js';
6
- export { deadlineSub, deadlinesSub, subscribeDeadline } from '../chunk-TAKPC6SD.js';
5
+ import '../chunk-NFOGYDRS.js';
6
+ export { deadlineSub, deadlinesSub, subscribeDeadline } from '../chunk-UGTC33VH.js';
7
7
  import '../chunk-VBDU4JL6.js';
8
8
  import '../chunk-4LPY67E4.js';
9
9
  import '../chunk-TXN2KFHF.js';
10
- import '../chunk-JAKM53JF.js';
11
- import '../chunk-UHAQ4PI6.js';
12
- import '../chunk-A73IJ27X.js';
13
- import '../chunk-DVNXEB7V.js';
14
- import '../chunk-RKVUVALV.js';
10
+ import '../chunk-7K7SEYCH.js';
11
+ import '../chunk-GFGOQIEC.js';
12
+ import '../chunk-2A5XTKQQ.js';
13
+ import '../chunk-R2YNDRGL.js';
14
+ import '../chunk-DEAPNMKO.js';
15
+ import '../chunk-BVFS3ICC.js';
16
+ import '../chunk-A3QPMX5M.js';
15
17
  //# sourceMappingURL=index.js.map
16
18
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
- import { restore } from './chunk-A73IJ27X.js';
2
- import { RuntimeDiscardNotice, detachWork, DispatchDiscardedError, QuiescenceTimeoutError, RuntimeDiscardedError, IdentityDropNotice, checkedStep, cmdContractOver, Outcome, cmdEdge, isFencedStore, DisposeTimeoutNotice, NoCellError, lookupCell, structuralHash, desiredSub, followUps } from './chunk-RKVUVALV.js';
1
+ import { restore } from './chunk-R2YNDRGL.js';
2
+ import { installedState } from './chunk-DEAPNMKO.js';
3
+ import { isFencedStore, RuntimeDiscardNotice, detachWork, acceptedTypes, DispatchDiscardedError, QuiescenceTimeoutError, IdentityDropNotice, checkedStep, cmdContractOver, Outcome, cmdEdge, SubFailedError, RuntimeDiscardedError, NoCellError, lookupCell, structuralHash, desiredSub, followUps, DrainTimeoutNotice, DisposeTimeoutNotice } from './chunk-A3QPMX5M.js';
3
4
 
4
5
  // src/internal/engine/builtins.ts
5
6
  function supervision(declared, machine) {
@@ -17,7 +18,7 @@ function supervision(declared, machine) {
17
18
  case "escalate":
18
19
  throw reduceError;
19
20
  default:
20
- loop.halt();
21
+ loop.halt(reduceError);
21
22
  throw reduceError;
22
23
  }
23
24
  }
@@ -98,6 +99,14 @@ function fencing() {
98
99
  }
99
100
  });
100
101
  }
102
+ function refuseFencedRunWithoutSink(store, onError) {
103
+ if (onError !== void 0 || store === void 0 || !isFencedStore(store)) {
104
+ return;
105
+ }
106
+ throw new Error(
107
+ "@demlik/tea: a run on a fenced store needs an `onError`. Without one, a lost fence ends the process and every run in it. Pass `onError` to `run`."
108
+ );
109
+ }
101
110
  function ports() {
102
111
  return (loop) => {
103
112
  const registry = /* @__PURE__ */ new Map();
@@ -133,8 +142,10 @@ function observation() {
133
142
  const bootHandlers = /* @__PURE__ */ new Set();
134
143
  let booted = false;
135
144
  return {
136
- commit(msg, state) {
145
+ installed() {
137
146
  loop.fanout(listeners, "listener", (listener) => listener());
147
+ },
148
+ commit(msg, state) {
138
149
  if (msg === void 0) {
139
150
  booted = true;
140
151
  loop.fanout(bootHandlers, "boot", (handler) => handler(state));
@@ -205,23 +216,57 @@ function terminality(isTerminal) {
205
216
  const holds = isTerminal ?? (() => false);
206
217
  return (loop) => {
207
218
  const waiters = /* @__PURE__ */ new Set();
219
+ let failure;
220
+ let stoppedBy;
208
221
  return {
222
+ subFailed(error) {
223
+ failure ??= { answer: new SubFailedError(error) };
224
+ },
225
+ halted(error) {
226
+ failure ??= { answer: error };
227
+ },
228
+ drained() {
229
+ if (failure === void 0) return;
230
+ stoppedBy = failure;
231
+ const state = loop.state();
232
+ const terminal = holds(state);
233
+ if (terminal && loop.saveStatus().type !== "saved") return;
234
+ const parked = [...waiters];
235
+ waiters.clear();
236
+ for (const waiter of parked) {
237
+ if (terminal) waiter.resolve(state);
238
+ else waiter.reject(stoppedBy.answer);
239
+ }
240
+ },
209
241
  commit(_msg, state) {
242
+ if (loop.saveStatus().type !== "saved" || waiters.size === 0 || !holds(state))
243
+ return;
244
+ const parked = [...waiters];
245
+ waiters.clear();
246
+ for (const waiter of parked) waiter.resolve(state);
247
+ },
248
+ saveFailed(state, error) {
210
249
  if (waiters.size === 0 || !holds(state)) return;
211
250
  const parked = [...waiters];
212
251
  waiters.clear();
213
- for (const resolve of parked) resolve(state);
252
+ for (const waiter of parked) waiter.reject(error);
214
253
  },
215
254
  handle: {
216
255
  result() {
217
256
  const current = loop.state();
218
- return holds(current) ? current : void 0;
257
+ return loop.saveStatus().type === "saved" && holds(current) ? current : void 0;
219
258
  },
220
259
  done() {
221
260
  const current = loop.state();
222
- if (holds(current)) return Promise.resolve(current);
223
- return new Promise((resolve) => {
224
- waiters.add(resolve);
261
+ if (holds(current)) {
262
+ const saved = loop.saveStatus();
263
+ if (saved.type === "saved") return Promise.resolve(current);
264
+ if (saved.type === "failed") return Promise.reject(saved.error);
265
+ } else if (stoppedBy !== void 0) {
266
+ return Promise.reject(stoppedBy.answer);
267
+ }
268
+ return new Promise((resolve, reject) => {
269
+ waiters.add({ resolve, reject });
225
270
  });
226
271
  }
227
272
  }
@@ -266,6 +311,7 @@ function builtinExtensions(machine, opts) {
266
311
 
267
312
  // src/internal/engine/loop.ts
268
313
  var liveWork = /* @__PURE__ */ Symbol("demlik-tea.liveWork");
314
+ var post = /* @__PURE__ */ Symbol("demlik-tea.post");
269
315
  function defaultOnError(error, _context) {
270
316
  if (error instanceof RuntimeDiscardNotice) {
271
317
  console.warn(error);
@@ -291,6 +337,21 @@ var RunningSub = class {
291
337
  };
292
338
  failed = false;
293
339
  };
340
+ var MsgLine = class {
341
+ counts = /* @__PURE__ */ new Map();
342
+ joined(type) {
343
+ this.counts.set(type, (this.counts.get(type) ?? 0) + 1);
344
+ }
345
+ reachedTurn(type) {
346
+ const left = (this.counts.get(type) ?? 0) - 1;
347
+ if (left > 0) this.counts.set(type, left);
348
+ else this.counts.delete(type);
349
+ }
350
+ /** The counts now, in a fresh object later turns cannot change. */
351
+ snapshot() {
352
+ return Object.fromEntries(this.counts);
353
+ }
354
+ };
294
355
  function startLoop(config) {
295
356
  const { idleCap, disposeTimeoutMs } = config;
296
357
  const onError = config.onError ?? defaultOnError;
@@ -312,18 +373,27 @@ function startLoop(config) {
312
373
  }
313
374
  }
314
375
  let state;
376
+ let installed;
377
+ let saveStatus = { type: "pending" };
315
378
  let bootError = null;
316
379
  let gate = "open";
317
- let inFlightCmds = 0;
380
+ const inFlight = /* @__PURE__ */ new Set();
318
381
  const services = {
319
382
  report,
320
383
  fanout,
321
- halt() {
322
- gate = "closed";
384
+ halt(error) {
385
+ const stopping = gate !== "open";
386
+ if (gate !== "abandoned") gate = "closed";
387
+ for (const ext of extensions) ext.halted?.(error);
388
+ if (!stopping) config.stopOnFailure({ kind: "halt", error });
323
389
  },
324
- state: () => state
390
+ state: () => state,
391
+ saveStatus: () => config.store === void 0 ? { type: "saved" } : saveStatus
325
392
  };
326
393
  const extensions = config.extensions.map((make) => make(services));
394
+ function lastStateReached() {
395
+ fanout(extensions, "sub", (ext) => ext.drained?.());
396
+ }
327
397
  const update = extensions.reduceRight(
328
398
  (next, ext) => ext.update ? ext.update(next) : next,
329
399
  config.reduce
@@ -333,6 +403,9 @@ function startLoop(config) {
333
403
  (inner, ext) => ext.store ? ext.store(inner) : inner,
334
404
  config.store
335
405
  );
406
+ const installedCallbacks = extensions.flatMap(
407
+ (ext) => ext.installed ? [ext.installed] : []
408
+ );
336
409
  const commitCallbacks = extensions.flatMap(
337
410
  (ext) => ext.commit ? [ext.commit] : []
338
411
  );
@@ -343,6 +416,7 @@ function startLoop(config) {
343
416
  { [detachWork]: detachInFlight }
344
417
  );
345
418
  let tail = Promise.resolve();
419
+ const line = new MsgLine();
346
420
  const subRegistry = /* @__PURE__ */ new Map();
347
421
  function stopSubs(ids) {
348
422
  for (const id of ids) {
@@ -366,27 +440,45 @@ function startLoop(config) {
366
440
  });
367
441
  pendingDisposals.add(settling);
368
442
  }
369
- async function drainDisposals() {
370
- if (pendingDisposals.size === 0) return;
371
- const outstanding = [...pendingDisposals];
443
+ async function withinLimit(work) {
372
444
  let timer;
373
445
  try {
374
- const outcome = await Promise.race([
375
- Promise.all(outstanding).then(() => "settled"),
446
+ return await Promise.race([
447
+ work.then(() => "settled"),
376
448
  new Promise((resolve) => {
377
449
  timer = setTimeout(() => resolve("timeout"), disposeTimeoutMs);
378
450
  })
379
451
  ]);
380
- if (outcome === "timeout") {
381
- report(
382
- new DisposeTimeoutNotice(outstanding.length, disposeTimeoutMs),
383
- "discard"
384
- );
385
- }
386
452
  } finally {
387
453
  if (timer !== void 0) clearTimeout(timer);
388
454
  }
389
455
  }
456
+ async function drainTail() {
457
+ if (await withinLimit(tail) === "settled") {
458
+ gate = "closed";
459
+ return;
460
+ }
461
+ report(
462
+ new DrainTimeoutNotice(
463
+ [...inFlight].flatMap(({ cmdType }) => cmdType ?? []),
464
+ inFlight.size,
465
+ disposeTimeoutMs,
466
+ line.snapshot()
467
+ ),
468
+ "discard"
469
+ );
470
+ gate = "abandoned";
471
+ }
472
+ async function drainDisposals() {
473
+ if (pendingDisposals.size === 0) return;
474
+ const outstanding = [...pendingDisposals];
475
+ if (await withinLimit(Promise.all(outstanding)) === "timeout") {
476
+ report(
477
+ new DisposeTimeoutNotice(outstanding.length, disposeTimeoutMs),
478
+ "discard"
479
+ );
480
+ }
481
+ }
390
482
  function reconcileSubs() {
391
483
  let firstError = null;
392
484
  const remember = (err) => {
@@ -452,10 +544,11 @@ function startLoop(config) {
452
544
  report(error, "sub");
453
545
  if (gate !== "open") return;
454
546
  gate = "draining";
455
- config.stopOnSubFailure(error);
547
+ for (const ext of extensions) ext.subFailed?.(error);
548
+ config.stopOnFailure({ kind: "sub", error });
456
549
  }
457
550
  function dispatchUnawaited(msg) {
458
- enqueueDispatch(msg).catch(reportUndelivered);
551
+ enqueueDispatch(msg, "loop").catch(reportUndelivered);
459
552
  }
460
553
  function reportUndelivered(error) {
461
554
  report(
@@ -463,10 +556,11 @@ function startLoop(config) {
463
556
  error instanceof DispatchDiscardedError ? "discard" : "follow-up"
464
557
  );
465
558
  }
466
- function trackInFlight(work) {
467
- inFlightCmds++;
559
+ function trackInFlight(work, cmdType) {
560
+ const entry = { cmdType };
561
+ inFlight.add(entry);
468
562
  return Promise.resolve(work).finally(() => {
469
- inFlightCmds--;
563
+ inFlight.delete(entry);
470
564
  });
471
565
  }
472
566
  function detachInFlight(work) {
@@ -478,22 +572,34 @@ function startLoop(config) {
478
572
  });
479
573
  }
480
574
  function callHandler(cmd, dispatch) {
481
- const handler = config.handlerFor(cmd.type);
575
+ const { type } = cmd;
576
+ const handler = config.handlerFor(type);
482
577
  if (handler === void 0) return Promise.resolve(void 0);
483
- return trackInFlight(handler(cmd, handlerCtx, dispatch));
578
+ return trackInFlight(handler(cmd, handlerCtx, dispatch), type);
484
579
  }
485
580
  async function runCmds(cmds) {
486
581
  for (const cmd of cmds) {
582
+ if (gate === "abandoned") return;
487
583
  const returned = await interpret(cmd, dispatchUnawaited);
488
- for (const follow of followUps(returned)) {
489
- enqueueDispatch(follow).catch(reportUndelivered);
490
- }
584
+ for (const follow of followUps(returned)) dispatchUnawaited(follow);
491
585
  }
492
586
  }
493
587
  async function commit(next, msg, cmds) {
494
588
  state = next;
495
- if (store) await store.save(next);
589
+ saveStatus = { type: "pending" };
590
+ if (store && gate !== "abandoned") {
591
+ try {
592
+ await store.save(next);
593
+ saveStatus = { type: "saved" };
594
+ } catch (error) {
595
+ saveStatus = { type: "failed", error };
596
+ for (const ext of extensions) ext.saveFailed?.(next, error);
597
+ throw error;
598
+ }
599
+ }
496
600
  reconcileSubs();
601
+ installed = next;
602
+ for (const callback of installedCallbacks) callback();
497
603
  try {
498
604
  await runCmds(cmds);
499
605
  } finally {
@@ -514,20 +620,39 @@ function startLoop(config) {
514
620
  const [initial, cmds] = storeless ?? config.init(await restore(store), config.ctx);
515
621
  await commit(initial, void 0, cmds);
516
622
  }
517
- function enqueueDispatch(msg) {
623
+ function enqueueDispatch(msg, origin = "caller") {
518
624
  if (gate !== "open") {
519
625
  return Promise.reject(
520
- gate === "draining" ? new DispatchDiscardedError(msg.type) : new RuntimeStoppedError(msg.type)
626
+ gate === "draining" || gate === "abandoned" && origin === "loop" ? new DispatchDiscardedError(msg.type) : new RuntimeStoppedError(msg.type)
521
627
  );
522
628
  }
629
+ return enqueueStep(msg, origin, stepDispatch);
630
+ }
631
+ function enqueueStep(msg, origin, step) {
632
+ line.joined(msg.type);
523
633
  const next = tail.then(() => {
634
+ line.reachedTurn(msg.type);
635
+ if (gate === "abandoned") {
636
+ if (origin === "caller") throw new DispatchDiscardedError(msg.type);
637
+ return;
638
+ }
524
639
  if (bootError !== null) throw bootError;
525
- return stepDispatch(msg);
640
+ return step(msg);
526
641
  });
527
642
  tail = next.catch(() => {
528
643
  });
529
644
  return next;
530
645
  }
646
+ function postMsg(msg, takes) {
647
+ if (gate !== "open") return "stopped";
648
+ if (!takes(state)) return "no_cell";
649
+ enqueueStep(
650
+ msg,
651
+ "tell",
652
+ (queued) => takes(state) ? stepDispatch(queued) : void 0
653
+ ).catch(reportUndelivered);
654
+ return "queued";
655
+ }
531
656
  async function drainToQuiescence() {
532
657
  for (let i = 0; i < idleCap; i++) {
533
658
  const observed = tail;
@@ -545,10 +670,30 @@ function startLoop(config) {
545
670
  tail = bootPromise.catch((err) => {
546
671
  bootError = err;
547
672
  });
673
+ async function teardown() {
674
+ if (gate === "open") gate = "draining";
675
+ if (inFlight.size > 0) {
676
+ report(new RuntimeDiscardedError(inFlight.size), "discard");
677
+ }
678
+ await drainTail();
679
+ lastStateReached();
680
+ stopSubs([...subRegistry.keys()]);
681
+ await drainDisposals();
682
+ if (store && state !== void 0 && bootError === null) {
683
+ try {
684
+ await store.save(state);
685
+ } catch (error) {
686
+ report(error, "stop-save");
687
+ }
688
+ }
689
+ }
690
+ let stopped;
548
691
  const core = {
549
- [liveWork]: () => ({ subs: subRegistry.size, cmds: inFlightCmds }),
692
+ [liveWork]: () => ({ subs: subRegistry.size, cmds: inFlight.size }),
693
+ [post]: postMsg,
694
+ [installedState]: () => installed,
550
695
  dispatch: dispatchToQuiescence,
551
- dispatchOnce: enqueueDispatch,
696
+ dispatchOnce: (msg) => enqueueDispatch(msg),
552
697
  getState() {
553
698
  return state;
554
699
  },
@@ -556,22 +701,9 @@ function startLoop(config) {
556
701
  // at `.then` time, after `handle` below is built.
557
702
  ready: bootPromise.then(() => handle),
558
703
  idle: drainToQuiescence,
559
- async stop() {
560
- if (gate === "open") gate = "draining";
561
- if (inFlightCmds > 0) {
562
- report(new RuntimeDiscardedError(inFlightCmds), "discard");
563
- }
564
- await tail;
565
- gate = "closed";
566
- stopSubs([...subRegistry.keys()]);
567
- await drainDisposals();
568
- if (store && state !== void 0 && bootError === null) {
569
- try {
570
- await store.save(state);
571
- } catch (error) {
572
- report(error, "stop-save");
573
- }
574
- }
704
+ stop() {
705
+ stopped ??= teardown();
706
+ return stopped;
575
707
  }
576
708
  };
577
709
  const handle = Object.assign(
@@ -581,6 +713,20 @@ function startLoop(config) {
581
713
  return handle;
582
714
  }
583
715
 
584
- export { RuntimeStoppedError, builtinExtensions, defaultOnError, liveWork, startLoop };
585
- //# sourceMappingURL=chunk-UHAQ4PI6.js.map
586
- //# sourceMappingURL=chunk-UHAQ4PI6.js.map
716
+ // src/internal/engine/tell.ts
717
+ function tellRun(machine, runtime, msg) {
718
+ const postable = runtime[post];
719
+ if (postable === void 0) {
720
+ throw new Error(
721
+ "@demlik/tea: `tell` needs a run handle from `run`; this object has no line to post to."
722
+ );
723
+ }
724
+ return postable(
725
+ msg,
726
+ (state) => acceptedTypes(machine, state).includes(msg.type)
727
+ );
728
+ }
729
+
730
+ export { RuntimeStoppedError, builtinExtensions, defaultOnError, liveWork, post, refuseFencedRunWithoutSink, startLoop, tellRun };
731
+ //# sourceMappingURL=chunk-2A5XTKQQ.js.map
732
+ //# sourceMappingURL=chunk-2A5XTKQQ.js.map