@demlik/tea 0.13.0 → 0.15.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 +11 -9
- package/dist/agent/index.d.ts +638 -45
- package/dist/agent/index.js +9 -19
- package/dist/{chunk-RH5EFRVJ.js → chunk-22POWGFA.js} +31 -5
- package/dist/chunk-22POWGFA.js.map +1 -0
- package/dist/{chunk-DMYGQB4O.js → chunk-3B5X7SBB.js} +23 -6
- package/dist/chunk-3B5X7SBB.js.map +1 -0
- package/dist/chunk-66PY5WYF.js +1816 -0
- package/dist/chunk-66PY5WYF.js.map +1 -0
- package/dist/{chunk-25LBDCBC.js → chunk-6IQPODUF.js} +2 -2
- package/dist/{chunk-25LBDCBC.js.map → chunk-6IQPODUF.js.map} +1 -1
- package/dist/chunk-AYS5N6WY.js +505 -0
- package/dist/chunk-AYS5N6WY.js.map +1 -0
- package/dist/{chunk-EWJP3YYP.js → chunk-HWVW4AXC.js} +3 -3
- package/dist/chunk-HWVW4AXC.js.map +1 -0
- package/dist/{chunk-O4MVQTEJ.js → chunk-L3TND7LA.js} +20 -16
- package/dist/chunk-L3TND7LA.js.map +1 -0
- package/dist/{chunk-J6527VRQ.js → chunk-LFZUK6J2.js} +3 -3
- package/dist/chunk-LFZUK6J2.js.map +1 -0
- package/dist/{chunk-D736QUGF.js → chunk-MYXFNWAJ.js} +4 -5
- package/dist/chunk-MYXFNWAJ.js.map +1 -0
- package/dist/{chunk-K575PT4S.js → chunk-PR6TB3BL.js} +15 -4
- package/dist/chunk-PR6TB3BL.js.map +1 -0
- package/dist/{chunk-YUXP55T2.js → chunk-Q5CQTBFA.js} +57 -7
- package/dist/chunk-Q5CQTBFA.js.map +1 -0
- package/dist/{batch-window/index.js → chunk-QPEVCJ5Q.js} +4 -8
- package/dist/chunk-QPEVCJ5Q.js.map +1 -0
- package/dist/chunk-TCLBCERH.js +1480 -0
- package/dist/chunk-TCLBCERH.js.map +1 -0
- package/dist/{chunk-FV3Z5MBE.js → chunk-TXN2KFHF.js} +14 -4
- package/dist/chunk-TXN2KFHF.js.map +1 -0
- package/dist/{chunk-MUUROGIH.js → chunk-V46HSVCK.js} +62 -12
- package/dist/chunk-V46HSVCK.js.map +1 -0
- package/dist/{chunk-Q2UM7BXU.js → chunk-VBDU4JL6.js} +2 -2
- package/dist/chunk-VBDU4JL6.js.map +1 -0
- package/dist/{chunk-ZVJZ72HY.js → chunk-VTQI2NHP.js} +25 -11
- package/dist/chunk-VTQI2NHP.js.map +1 -0
- package/dist/{core-Co0LWkIA.d.ts → core-DnzqWy6u.d.ts} +72 -23
- package/dist/devtools/index.d.ts +2 -2
- package/dist/devtools/index.js +207 -2
- package/dist/devtools/index.js.map +1 -1
- package/dist/do/index.d.ts +43 -29
- package/dist/do/index.js +36 -25
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-C6uwzjb5.d.ts → durable-effects-CWzA-o2B.d.ts} +2 -2
- package/dist/extension/index.d.ts +300 -3
- package/dist/extension/index.js +596 -5
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +1284 -0
- package/dist/flow/index.js +732 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/idempotency/index.d.ts +90 -116
- package/dist/idempotency/index.js +4 -1
- package/dist/index-Bc40JWVl.d.ts +131 -0
- package/dist/{batch-window/index.d.ts → index-C2DwkjlP.d.ts} +7 -12
- package/dist/index-CA_7I5g9.d.ts +806 -0
- package/dist/index-CSyMYDAB.d.ts +69 -0
- package/dist/{fan-out/index.d.ts → index-CuNO9m8i.d.ts} +7 -9
- package/dist/{debounce/index.d.ts → index-DHSUmQTR.d.ts} +7 -7
- package/dist/{recorder/index.d.ts → index-DrIRb_Ch.d.ts} +4 -7
- package/dist/index-HSYgG2Yt.d.ts +380 -0
- package/dist/{monitored-run/index.d.ts → index-KXqTRJyb.d.ts} +37 -13
- package/dist/{snapshot/index.d.ts → index-ZCMhnVKN.d.ts} +15 -78
- package/dist/index.d.ts +771 -6
- package/dist/index.js +1 -2
- package/dist/jev/index.d.ts +646 -0
- package/dist/jev/index.js +485 -0
- package/dist/jev/index.js.map +1 -0
- package/dist/machine-DGlPWCWI.d.ts +1781 -0
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +12 -5
- package/dist/machine-viz/index.js.map +1 -1
- package/dist/mem/index.d.ts +18 -3
- package/dist/mem/index.js +22 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +104 -6
- package/dist/node/index.js +132 -9
- package/dist/node/index.js.map +1 -1
- package/dist/{paginator → paginate}/index.d.ts +140 -6
- package/dist/paginate/index.js +7 -0
- package/dist/parity/index.d.ts +4 -10
- package/dist/parity/index.js +4 -7
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +247 -5
- package/dist/pbt/index.js +124 -5
- package/dist/pbt/index.js.map +1 -1
- package/dist/{trace-replay → persistence}/index.d.ts +7 -5
- package/dist/persistence/index.js +6 -0
- package/dist/{protocol-B5YmJr0d.d.ts → protocol-D8VbH-VH.d.ts} +10 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -2
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +744 -0
- package/dist/resilience/index.js +657 -0
- package/dist/resilience/index.js.map +1 -0
- package/dist/retry-backoff/index.d.ts +3 -2
- package/dist/retry-backoff/index.js +1 -1
- package/dist/runtime-types-CMF13BBh.d.ts +998 -0
- package/dist/testing/index.d.ts +147 -3
- package/dist/testing/index.js +95 -3
- package/dist/testing/index.js.map +1 -1
- package/dist/{throttled-input → timing}/index.d.ts +143 -12
- package/dist/{throttled-input → timing}/index.js +57 -12
- package/dist/timing/index.js.map +1 -0
- package/dist/{types-XS_miRnB.d.ts → types-BseNPIhr.d.ts} +1 -1
- package/dist/work-queue/index.d.ts +130 -54
- package/dist/work-queue/index.js +3 -2
- package/dist/work-queue/index.js.map +1 -1
- package/package.json +45 -218
- package/dist/authed-call/index.d.ts +0 -120
- package/dist/authed-call/index.js +0 -134
- package/dist/authed-call/index.js.map +0 -1
- package/dist/await-terminal/index.d.ts +0 -101
- package/dist/await-terminal/index.js +0 -76
- package/dist/await-terminal/index.js.map +0 -1
- package/dist/batch-window/index.js.map +0 -1
- package/dist/cache/index.d.ts +0 -152
- package/dist/cache/index.js +0 -8
- package/dist/chart/index.d.ts +0 -218
- package/dist/chart/index.js +0 -16
- package/dist/chart/index.js.map +0 -1
- package/dist/chart/inspect/index.d.ts +0 -86
- package/dist/chart/inspect/index.js +0 -5
- package/dist/chart/inspect/react.d.ts +0 -113
- package/dist/chart/inspect/react.js +0 -11
- package/dist/chart/inspect/react.js.map +0 -1
- package/dist/chart/inspect/styles.css +0 -286
- package/dist/chart/lane/index.d.ts +0 -362
- package/dist/chart/lane/index.js +0 -9
- package/dist/chart/lane/index.js.map +0 -1
- package/dist/chart/lane/react.d.ts +0 -84
- package/dist/chart/lane/react.js +0 -559
- package/dist/chart/lane/react.js.map +0 -1
- package/dist/chart/lane/server.d.ts +0 -148
- package/dist/chart/lane/server.js +0 -209
- package/dist/chart/lane/server.js.map +0 -1
- package/dist/chart/lane/styles.css +0 -498
- package/dist/chart/lane/viewer/assets/abnfDiagram-N423BO3Z-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/architectureDiagram-T3A2C74G-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/blockDiagram-VBNYF7ZC-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/c4Diagram-5PPSVZJV-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-2GRJ4B5K-HdDf8SI3.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-5RXB4S5H-CcNtpUd7.js +0 -231
- package/dist/chart/lane/viewer/assets/chunk-5VM5RSS4-BP4fRS_5.js +0 -15
- package/dist/chart/lane/viewer/assets/chunk-KBJHAD2P-DZOw7xU6.js +0 -1
- package/dist/chart/lane/viewer/assets/chunk-NSK5VX7P-CBCyTZMs.js +0 -128
- package/dist/chart/lane/viewer/assets/classDiagram-JCYQIIEL-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/classDiagram-v2-OCEON4UE-D4uAiFr8.js +0 -206
- package/dist/chart/lane/viewer/assets/cose-bilkent-JH36ORCC-DOwCrGXH.js +0 -1
- package/dist/chart/lane/viewer/assets/cynefinDiagram-MW4NZA55-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/dagre-K_PewWfS.js +0 -1
- package/dist/chart/lane/viewer/assets/dagre-VZM6K2ZE-831qZcA8.js +0 -4
- package/dist/chart/lane/viewer/assets/diagram-7IWD3JNH-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-B4RE2ZJO-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-LBJQPF4R-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-Q27KOJAE-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/diagram-UB23O5K3-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ebnfDiagram-BXEA7PRR-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/erDiagram-JOGREHBK-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/flowDiagram-UKHOOZJN-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ganttDiagram-PKOTCBZU-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/gitGraphDiagram-DS77QQ5N-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/index-9SkH5NA3.css +0 -1
- package/dist/chart/lane/viewer/assets/index-Bgt4sXpW.js +0 -195
- package/dist/chart/lane/viewer/assets/infoDiagram-6WML65LV-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/ishikawaDiagram-WSZJBQD7-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/journeyDiagram-NVQOT4AX-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/kanban-definition-27J2QSJJ-G7PaPzy-.js +0 -89
- package/dist/chart/lane/viewer/assets/katex-DOwCrGXH.js +0 -1
- package/dist/chart/lane/viewer/assets/mindmap-definition-FAOFIHXS-CGj9nrVk.js +0 -96
- package/dist/chart/lane/viewer/assets/path-fybaL0A-.js +0 -1
- package/dist/chart/lane/viewer/assets/pegDiagram-VL7TDLO6-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/pieDiagram-7S7Q4E2Y-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/quadrantDiagram-CIZ2JOQS-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/railroadDiagram-AXF67PYL-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/requirementDiagram-LRYGKXZP-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/sankeyDiagram-W5VNT64P-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/sequenceDiagram-SI44F4Z6-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/stateDiagram-OKZ733FA-B_88AzBS.js +0 -1
- package/dist/chart/lane/viewer/assets/stateDiagram-v2-UEYNNEHI-CR5gcgq6.js +0 -1
- package/dist/chart/lane/viewer/assets/swimlanes-SLNWSIFB-B4xK-rwg.js +0 -1
- package/dist/chart/lane/viewer/assets/swimlanesDiagram-ULZ7WXOC-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/timeline-definition-Z64GVDOM-CCv52MfY.js +0 -120
- package/dist/chart/lane/viewer/assets/vennDiagram-T6HMQDX7-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/wardleyDiagram-T6FBY63Y-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/assets/xychartDiagram-ELKLHX3M-C729LI31.js +0 -1
- package/dist/chart/lane/viewer/index.html +0 -15
- package/dist/chart/report/index.d.ts +0 -117
- package/dist/chart/report/index.js +0 -32
- package/dist/chart/report/index.js.map +0 -1
- package/dist/chunk-2YFLOCWX.js +0 -15
- package/dist/chunk-2YFLOCWX.js.map +0 -1
- package/dist/chunk-3I52QZZ5.js +0 -519
- package/dist/chunk-3I52QZZ5.js.map +0 -1
- package/dist/chunk-3ZAFFRRB.js +0 -457
- package/dist/chunk-3ZAFFRRB.js.map +0 -1
- package/dist/chunk-4EQZ76JC.js +0 -33
- package/dist/chunk-4EQZ76JC.js.map +0 -1
- package/dist/chunk-4KLG43GY.js +0 -223
- package/dist/chunk-4KLG43GY.js.map +0 -1
- package/dist/chunk-7FRZ2CT2.js +0 -424
- package/dist/chunk-7FRZ2CT2.js.map +0 -1
- package/dist/chunk-AZZLE7XR.js +0 -62
- package/dist/chunk-AZZLE7XR.js.map +0 -1
- package/dist/chunk-BYQUGDD3.js +0 -16
- package/dist/chunk-BYQUGDD3.js.map +0 -1
- package/dist/chunk-CIQG5L42.js +0 -685
- package/dist/chunk-CIQG5L42.js.map +0 -1
- package/dist/chunk-CLM6ERNO.js +0 -61
- package/dist/chunk-CLM6ERNO.js.map +0 -1
- package/dist/chunk-COS5MSTO.js +0 -88
- package/dist/chunk-COS5MSTO.js.map +0 -1
- package/dist/chunk-CRPZJVD2.js +0 -71
- package/dist/chunk-CRPZJVD2.js.map +0 -1
- package/dist/chunk-D62FDQKE.js +0 -53
- package/dist/chunk-D62FDQKE.js.map +0 -1
- package/dist/chunk-D736QUGF.js.map +0 -1
- package/dist/chunk-DGE4PV4A.js +0 -26
- package/dist/chunk-DGE4PV4A.js.map +0 -1
- package/dist/chunk-DMYGQB4O.js.map +0 -1
- package/dist/chunk-EWJP3YYP.js.map +0 -1
- package/dist/chunk-FV3Z5MBE.js.map +0 -1
- package/dist/chunk-HCEVHFJ7.js +0 -54
- package/dist/chunk-HCEVHFJ7.js.map +0 -1
- package/dist/chunk-J6527VRQ.js.map +0 -1
- package/dist/chunk-JELSZN25.js +0 -47
- package/dist/chunk-JELSZN25.js.map +0 -1
- package/dist/chunk-K575PT4S.js.map +0 -1
- package/dist/chunk-L57ZOT5K.js +0 -184
- package/dist/chunk-L57ZOT5K.js.map +0 -1
- package/dist/chunk-MU6MCIAC.js +0 -15
- package/dist/chunk-MU6MCIAC.js.map +0 -1
- package/dist/chunk-MUUROGIH.js.map +0 -1
- package/dist/chunk-NR7BU22N.js +0 -758
- package/dist/chunk-NR7BU22N.js.map +0 -1
- package/dist/chunk-O4MVQTEJ.js.map +0 -1
- package/dist/chunk-OOYD44JI.js +0 -317
- package/dist/chunk-OOYD44JI.js.map +0 -1
- package/dist/chunk-Q2UM7BXU.js.map +0 -1
- package/dist/chunk-RH5EFRVJ.js.map +0 -1
- package/dist/chunk-S2EGRPCM.js +0 -47
- package/dist/chunk-S2EGRPCM.js.map +0 -1
- package/dist/chunk-SINXE47Y.js +0 -55
- package/dist/chunk-SINXE47Y.js.map +0 -1
- package/dist/chunk-UF4MZSLT.js +0 -53
- package/dist/chunk-UF4MZSLT.js.map +0 -1
- package/dist/chunk-UOC2PL46.js +0 -13
- package/dist/chunk-UOC2PL46.js.map +0 -1
- package/dist/chunk-VYFAZN3F.js +0 -372
- package/dist/chunk-VYFAZN3F.js.map +0 -1
- package/dist/chunk-WRAA7GVZ.js +0 -209
- package/dist/chunk-WRAA7GVZ.js.map +0 -1
- package/dist/chunk-XGELA764.js +0 -163
- package/dist/chunk-XGELA764.js.map +0 -1
- package/dist/chunk-YUXP55T2.js.map +0 -1
- package/dist/chunk-YWIKDKHY.js +0 -307
- package/dist/chunk-YWIKDKHY.js.map +0 -1
- package/dist/chunk-ZJ5AOYBH.js +0 -8
- package/dist/chunk-ZJ5AOYBH.js.map +0 -1
- package/dist/chunk-ZSUUOEPL.js +0 -731
- package/dist/chunk-ZSUUOEPL.js.map +0 -1
- package/dist/chunk-ZVJZ72HY.js.map +0 -1
- package/dist/circuit-breaker/index.d.ts +0 -197
- package/dist/circuit-breaker/index.js +0 -3
- package/dist/circuit-breaker/index.js.map +0 -1
- package/dist/deadline/index.d.ts +0 -199
- package/dist/deadline/index.js +0 -6
- package/dist/deadline/index.js.map +0 -1
- package/dist/debounce/index.js +0 -3
- package/dist/debounce/index.js.map +0 -1
- package/dist/extension/react.d.ts +0 -121
- package/dist/extension/react.js +0 -75
- package/dist/extension/react.js.map +0 -1
- package/dist/extension/subs/index.d.ts +0 -45
- package/dist/extension/subs/index.js +0 -80
- package/dist/extension/subs/index.js.map +0 -1
- package/dist/extension/test-utils.d.ts +0 -128
- package/dist/extension/test-utils.js +0 -277
- package/dist/extension/test-utils.js.map +0 -1
- package/dist/fan-out/index.js +0 -5
- package/dist/fan-out/index.js.map +0 -1
- package/dist/fold-BJPdjc9w.d.ts +0 -435
- package/dist/graph-B3L5e0N1.d.ts +0 -923
- package/dist/idempotency/adapter.d.ts +0 -62
- package/dist/idempotency/adapter.js +0 -4
- package/dist/idempotency/adapter.js.map +0 -1
- package/dist/idempotent-intake/index.d.ts +0 -188
- package/dist/idempotent-intake/index.js +0 -7
- package/dist/idempotent-intake/index.js.map +0 -1
- package/dist/journal/index.d.ts +0 -373
- package/dist/journal/index.js +0 -3
- package/dist/journal/index.js.map +0 -1
- package/dist/llm-call/index.d.ts +0 -311
- package/dist/llm-call/index.js +0 -16
- package/dist/llm-call/index.js.map +0 -1
- package/dist/machine-BtFWQmmq.d.ts +0 -804
- package/dist/monitored-run/index.js +0 -10
- package/dist/monitored-run/index.js.map +0 -1
- package/dist/paginated-walk/index.d.ts +0 -235
- package/dist/paginated-walk/index.js +0 -16
- package/dist/paginated-walk/index.js.map +0 -1
- package/dist/paginator/index.js +0 -3
- package/dist/paginator/index.js.map +0 -1
- package/dist/pbt/arbitraries/index.d.ts +0 -136
- package/dist/pbt/arbitraries/index.js +0 -3
- package/dist/pbt/arbitraries/index.js.map +0 -1
- package/dist/pbt/runners/index.d.ts +0 -118
- package/dist/pbt/runners/index.js +0 -5
- package/dist/pbt/runners/index.js.map +0 -1
- package/dist/poller/index.d.ts +0 -352
- package/dist/poller/index.js +0 -115
- package/dist/poller/index.js.map +0 -1
- package/dist/prediction/index.d.ts +0 -131
- package/dist/prediction/index.js +0 -4
- package/dist/prediction/index.js.map +0 -1
- package/dist/pure/index.d.ts +0 -3
- package/dist/pure/index.js +0 -4
- package/dist/pure/index.js.map +0 -1
- package/dist/rate-limit/index.d.ts +0 -115
- package/dist/rate-limit/index.js +0 -3
- package/dist/rate-limit/index.js.map +0 -1
- package/dist/reconciler/index.d.ts +0 -193
- package/dist/reconciler/index.js +0 -164
- package/dist/reconciler/index.js.map +0 -1
- package/dist/recorder/index.js +0 -3
- package/dist/recorder/index.js.map +0 -1
- package/dist/reducer-4GNR6b8B.d.ts +0 -447
- package/dist/resilient-call/index.d.ts +0 -320
- package/dist/resilient-call/index.js +0 -14
- package/dist/resilient-call/index.js.map +0 -1
- package/dist/retry-to-success/index.d.ts +0 -109
- package/dist/retry-to-success/index.js +0 -37
- package/dist/retry-to-success/index.js.map +0 -1
- package/dist/run-pAcmBpah.d.ts +0 -713
- package/dist/runtime-types-CNLmhWif.d.ts +0 -517
- package/dist/saga/index.d.ts +0 -241
- package/dist/saga/index.js +0 -150
- package/dist/saga/index.js.map +0 -1
- package/dist/samples-B3My5oh2.d.ts +0 -59
- package/dist/snapshot/index.js +0 -5
- package/dist/snapshot/index.js.map +0 -1
- package/dist/subs/index.d.ts +0 -472
- package/dist/subs/index.js +0 -7
- package/dist/subs/index.js.map +0 -1
- package/dist/throttle/index.d.ts +0 -134
- package/dist/throttle/index.js +0 -3
- package/dist/throttle/index.js.map +0 -1
- package/dist/throttled-input/index.js.map +0 -1
- package/dist/token-refresh/index.d.ts +0 -216
- package/dist/token-refresh/index.js +0 -6
- package/dist/token-refresh/index.js.map +0 -1
- package/dist/trace-replay/index.js +0 -6
- package/dist/trace-replay/index.js.map +0 -1
- package/dist/with-deadline/index.d.ts +0 -197
- package/dist/with-deadline/index.js +0 -111
- package/dist/with-deadline/index.js.map +0 -1
- package/dist/with-resilience/index.d.ts +0 -225
- package/dist/with-resilience/index.js +0 -240
- package/dist/with-resilience/index.js.map +0 -1
- package/dist/with-telemetry/index.d.ts +0 -150
- package/dist/with-telemetry/index.js +0 -70
- package/dist/with-telemetry/index.js.map +0 -1
- package/dist/work-queue/adapter.d.ts +0 -84
- package/dist/work-queue/adapter.js +0 -4
- package/dist/work-queue/adapter.js.map +0 -1
- package/dist/work-queue/ops.d.ts +0 -71
- package/dist/work-queue/ops.js +0 -3
- package/dist/work-queue/ops.js.map +0 -1
- package/dist/workflow/index.d.ts +0 -485
- package/dist/workflow/index.js +0 -244
- package/dist/workflow/index.js.map +0 -1
- /package/dist/{cache → paginate}/index.js.map +0 -0
- /package/dist/{chart/inspect → persistence}/index.js.map +0 -0
|
@@ -0,0 +1,1781 @@
|
|
|
1
|
+
import { C as Cmd, j as Tagged, I as Interpret, A as AnyCmdDef, a as CmdOf, b as CmdDef, e as Settled, i as NoCtx, O as OkOf, E as ErrOf, k as MalformedResult, l as CmdValue, g as PortEmitter, m as Requirements, n as TaggedError, M as Machine } from './core-DnzqWy6u.js';
|
|
2
|
+
import { a as MonitoredRunState, R as RunFailure, M as MonitoredRunCmd } from './index-KXqTRJyb.js';
|
|
3
|
+
import { R as RunCmd, S as SucceedMsg, F as FailMsg, a as ResilientState } from './index-CA_7I5g9.js';
|
|
4
|
+
import { D as DeadlineExceeded, a as DeadlineSub } from './index-HSYgG2Yt.js';
|
|
5
|
+
import { M as MsgType, a as MsgTypeValue } from './protocol-D8VbH-VH.js';
|
|
6
|
+
import { F as FanOutState } from './index-CuNO9m8i.js';
|
|
7
|
+
import { RetryPolicy, AnyRetryPolicy } from './retry-backoff/index.js';
|
|
8
|
+
import { Result } from 'better-result';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
* internal/llm-call — `resilient-call` + structured-output parse + a typed
|
|
14
|
+
* failure variant, around a purpose-discriminated LLM invocation. Internal
|
|
15
|
+
* since #49 — not published on any subpath; `@demlik/tea/agent` re-exports
|
|
16
|
+
* the types a consumer meets.
|
|
17
|
+
*
|
|
18
|
+
* This is the ~60-line `call_llm` handler from the audit-agent seed
|
|
19
|
+
* (`interpret.ts` / `effects.ts`) collapsed into the uniform L2 knob: a config
|
|
20
|
+
* object, the resilient-call slice it inherits, and one interpret handler that
|
|
21
|
+
* assembles messages, binds the structured-output schema for the call's
|
|
22
|
+
* `purpose`, invokes the model (retry composed from `../resilient-call`, NOT
|
|
23
|
+
* reinvented), parses the output, and RETURNS the enriched resilient settle Msg
|
|
24
|
+
* (`resilient_ok` / `resilient_err`) so it re-enters the host reducer and drives
|
|
25
|
+
* the inherited succeed/fail → backoff → onTimer loop.
|
|
26
|
+
*
|
|
27
|
+
* ## What it adds over `../resilient-call`
|
|
28
|
+
*
|
|
29
|
+
* - **Two DI ports** (the seed's two seams):
|
|
30
|
+
* 1. `model` — either `async (messages) => answer`, the plain-function
|
|
31
|
+
* port (#58), or `(modelId) => LLM`, the model factory that binds the
|
|
32
|
+
* structured-output schema itself. Tests pass a fake so the handler
|
|
33
|
+
* runs end-to-end without touching a real provider; the production
|
|
34
|
+
* worker wires `createChatModel(env, …)`.
|
|
35
|
+
* 2. `loadMessages: Loader` — the SDK / message loader. The seed lazy-
|
|
36
|
+
* imports `@langchain/core/messages` (a top-level `import type` blows
|
|
37
|
+
* up the workers test runner); the loader keeps that import lazy and
|
|
38
|
+
* stubbable. It produces the `BaseMessage[]` for a given call.
|
|
39
|
+
* - **Structured-output parse** — each `purpose` maps to a `Schema` in
|
|
40
|
+
* `config.schemas`. The handler binds `model.withStructuredOutput(schema)`
|
|
41
|
+
* so the invoke resolves to a typed object; a `Schema.parse` that throws
|
|
42
|
+
* becomes a `resilient_err` carrying an `LlmErr` (a parse failure is a
|
|
43
|
+
* failure, not a stall — "errors are data"). `withStructuredOutput` is the
|
|
44
|
+
* seed's brain-only path; the fake model in tests returns the typed object
|
|
45
|
+
* directly.
|
|
46
|
+
* - **A typed failure variant** — every failure (model throw, timeout-via-
|
|
47
|
+
* retry-exhaustion, schema parse) is surfaced as the `LlmErr` carried on the
|
|
48
|
+
* enriched `resilient_err` settle Msg, tagged with the `purpose` so the
|
|
49
|
+
* consumer's reducer routes per-stage. The seed dispatched
|
|
50
|
+
* `llm_failed{purpose}`; the consumer rebuilds that shape in its reducer arm
|
|
51
|
+
* from `m.error: LlmErr`.
|
|
52
|
+
*
|
|
53
|
+
* ## Inheriting resilient-call by composition (not reinvention)
|
|
54
|
+
*
|
|
55
|
+
* The retry / backoff lives in `../resilient-call`: `createLlmCall` builds a
|
|
56
|
+
* `createResilientCall` knob over the model-invoke port and DELEGATES `init`,
|
|
57
|
+
* `attempt`, `succeed`, `fail`, `onTimer`, `subs` to it verbatim — the slice is
|
|
58
|
+
* literally resilient-call's slice (the spec's "retry lives here"). There is no
|
|
59
|
+
* second backoff implementation here. The handler runs the invoke through the
|
|
60
|
+
* resilient-call handler so a transient model failure backs off exactly as a
|
|
61
|
+
* resilient HTTP call would; the `purpose`-branch + structured parse wrap the
|
|
62
|
+
* port the resilient handler drives.
|
|
63
|
+
*
|
|
64
|
+
* ## The two non-negotiables (canon, inherited)
|
|
65
|
+
*
|
|
66
|
+
* - **Durable** — the slice is resilient-call's plain-data slice (a Model
|
|
67
|
+
* field). Input carried on the `resilient_run` Cmd is the plain
|
|
68
|
+
* `LlmCall` request (purpose + modelId + payload) — no closures.
|
|
69
|
+
* - **Replayable** — every transition is a resilient-call verb; the handler
|
|
70
|
+
* is the only impurity, and the one clock read is `Date.now()` at the
|
|
71
|
+
* effect boundary (inside the handler), exactly as the seed stamped its
|
|
72
|
+
* `llm_responded` / `llm_failed` Msgs.
|
|
73
|
+
*
|
|
74
|
+
* ## Typical wiring
|
|
75
|
+
*
|
|
76
|
+
* const llm = createLlmCall<MyPurpose, MyOutputs>({
|
|
77
|
+
* model: (id) => createChatModel(env, id),
|
|
78
|
+
* schemas: { plan: planSchema, report: reportSchema },
|
|
79
|
+
* retry: defaultRetryPolicy,
|
|
80
|
+
* loadMessages: defaultMessagesLoader,
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* // `mountResilientCall` pre-assembles the wiring. The settle cells run the
|
|
84
|
+
* // inherited verb and hand the ALREADY-settled model to `onOk` / `onErr`,
|
|
85
|
+
* // so the fold-before-settle order that wedges the slice at `running` is
|
|
86
|
+
* // not something a mounted cell can express; `subscribe` and `interpret`
|
|
87
|
+
* // ride along, so neither can be forgotten.
|
|
88
|
+
* const mounted = mountResilientCall(llm, {
|
|
89
|
+
* slice: "resilience",
|
|
90
|
+
* attempt: {
|
|
91
|
+
* on: "call_llm",
|
|
92
|
+
* run: (slice, m: CallLlm) => llm.attempt(slice, m.input, m.at),
|
|
93
|
+
* },
|
|
94
|
+
* onOk: (s, m) => [{ ...s, output: m.result.output }, []],
|
|
95
|
+
* onErr: (s, m) => [{ ...s, failure: m.error }, []],
|
|
96
|
+
* // A deadline-exceeded call settles inside the slice and emits no
|
|
97
|
+
* // settle Msg, so it reaches no `onErr`. This is its fold.
|
|
98
|
+
* onDeadline: (s, m) => [{ ...s, failure: m.error }, []],
|
|
99
|
+
* });
|
|
100
|
+
*
|
|
101
|
+
* // in the machine:
|
|
102
|
+
* init: () => [{ ...mounted.init(), output: null, failure: null }, []],
|
|
103
|
+
* update: { ...mounted.update },
|
|
104
|
+
* subscriptions: mounted.subscriptions,
|
|
105
|
+
* subscribe: mounted.subscribe,
|
|
106
|
+
* interpret: mounted.interpret,
|
|
107
|
+
*
|
|
108
|
+
* The slice stays a plain readable field at `resilience`, and every verb above
|
|
109
|
+
* is still exported: a consumer that wants a settle cell the mount cannot
|
|
110
|
+
* express writes that one cell with `llm.succeed` / `llm.fail` / `liftLlmCall`
|
|
111
|
+
* and spreads the rest.
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The minimal structured-output schema contract: `parse(unknown) => T`, the
|
|
116
|
+
* zod-style call the handler uses to validate the model's output before it
|
|
117
|
+
* settles `resilient_ok`. A throwing `parse` (the zod contract on invalid
|
|
118
|
+
* input) propagates out of `invokeOne` and is caught by the resilient handler,
|
|
119
|
+
* which settles `resilient_err` (enriched to `LlmErr`) — a parse failure is a
|
|
120
|
+
* failure, never a silent pass. Structural so a real `z.ZodType<T>` satisfies
|
|
121
|
+
* it without an import.
|
|
122
|
+
*/
|
|
123
|
+
interface Schema<T> {
|
|
124
|
+
/** Validate + narrow `value` to `T`, or throw on mismatch (the zod contract). */
|
|
125
|
+
parse(value: unknown): T;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The minimal chat-model contract every model the handler talks to must
|
|
129
|
+
* satisfy — the seed's `InjectableChatModel`, trimmed to the one operation
|
|
130
|
+
* llm-call drives for brain-only stages:
|
|
131
|
+
*
|
|
132
|
+
* `withStructuredOutput(schema)` → a runnable whose `invoke(messages)`
|
|
133
|
+
* resolves to a typed object matching `schema`.
|
|
134
|
+
*
|
|
135
|
+
* `BaseChatModel` from `@langchain/core` is the runtime type; this surface
|
|
136
|
+
* names only what llm-call calls so a test fake can ignore the rest. Generic
|
|
137
|
+
* over the message shape `Msg` so a consumer's loader and model agree on it
|
|
138
|
+
* without llm-call inspecting messages.
|
|
139
|
+
*/
|
|
140
|
+
interface Llm<Msg> {
|
|
141
|
+
withStructuredOutput<T>(schema: Schema<T>): {
|
|
142
|
+
invoke(messages: readonly Msg[]): Promise<T>;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Build the `Msg[]` the handler hands to the bound model for a given call. The
|
|
147
|
+
* SDK / message-assembly seam — the seed's `buildBaseMessages` + the lazy
|
|
148
|
+
* `loadMessages` loader rolled into one injected port. Async because the seed
|
|
149
|
+
* lazy-imports the SDK (a top-level `import type` of the langchain messages
|
|
150
|
+
* package explodes the workers test runner). Receives the full `LlmCall` so it
|
|
151
|
+
* can branch on `purpose` exactly as the seed's `buildBaseMessages` did.
|
|
152
|
+
*/
|
|
153
|
+
type MessageLoader<P extends string, Msg> = (call: LlmCall<P>) => Promise<readonly Msg[]>;
|
|
154
|
+
/**
|
|
155
|
+
* The model factory — the first DI port. `(modelId) => Llm`. Tests pass a fake
|
|
156
|
+
* builder; production wires `createChatModel(env, getModelConfig(id))`. `null`
|
|
157
|
+
* means "the host's default model" (the seed's `string | null`).
|
|
158
|
+
*/
|
|
159
|
+
type ModelFactory<Msg> = (modelId: string | null) => Llm<Msg>;
|
|
160
|
+
/**
|
|
161
|
+
* The plain-function model port — the common path. One async function
|
|
162
|
+
* from the assembled messages to the model's answer; the handler validates the
|
|
163
|
+
* answer through the purpose's `Schema` exactly as it re-validates the
|
|
164
|
+
* structured-output path, so a malformed answer is a `resilient_err`, never a
|
|
165
|
+
* corrupt success. `ModelFactory` → `withStructuredOutput(schema).invoke(...)`
|
|
166
|
+
* stays the advanced form for a model that binds the schema itself.
|
|
167
|
+
*
|
|
168
|
+
* `T` is the answer type the schema narrows to — for the agent, an `AgentTurn`.
|
|
169
|
+
*/
|
|
170
|
+
type PlainModel<Msg, T = unknown> = (messages: readonly Msg[]) => Promise<T>;
|
|
171
|
+
/**
|
|
172
|
+
* Either model port. A bare `async` function is read as a `PlainModel` (see
|
|
173
|
+
* {@link asModelFactory}); a sync function is the factory.
|
|
174
|
+
*/
|
|
175
|
+
type ModelPort<Msg, T = unknown> = ModelFactory<Msg> | PlainModel<Msg, T>;
|
|
176
|
+
/**
|
|
177
|
+
* Lift a plain-function model into the `ModelFactory` port. The factory ignores
|
|
178
|
+
* `modelId` (the function IS the model) and its `Llm` runs the bound schema over
|
|
179
|
+
* the function's answer, so both ports meet the handler as one shape.
|
|
180
|
+
*
|
|
181
|
+
* Reach for this explicitly when the function is not declared `async` — a sync
|
|
182
|
+
* function that returns a promise (`(m) => client.chat(m)`) carries no runtime
|
|
183
|
+
* mark that tells it apart from a factory. Passed bare, `asModelFactory`
|
|
184
|
+
* refuses it on the first call with an `LlmErr` whose reason is
|
|
185
|
+
* {@link PLAIN_MODEL_MISROUTE_REASON}.
|
|
186
|
+
*/
|
|
187
|
+
declare function plainModel<Msg, T>(fn: PlainModel<Msg, T>): ModelFactory<Msg>;
|
|
188
|
+
/**
|
|
189
|
+
* The reason an `LlmErr` carries when a sync promise-returning function was
|
|
190
|
+
* passed as `model` bare — the one runtime shape neither port can own.
|
|
191
|
+
*/
|
|
192
|
+
declare const PLAIN_MODEL_MISROUTE_REASON: string;
|
|
193
|
+
/**
|
|
194
|
+
* One LLM call request — the resilient-call `input` for this module, carried on
|
|
195
|
+
* the `resilient_run` Cmd as plain data — no closures, so it survives
|
|
196
|
+
* persistence and replay. Purpose-discriminated: a `purpose` selecting the
|
|
197
|
+
* schema + prompt assembly, the `model` id, and the opaque per-purpose
|
|
198
|
+
* `payload` the loader consumes.
|
|
199
|
+
*
|
|
200
|
+
* `key` defaults to `purpose` when the consumer calls `attempt(s, purpose, …)`,
|
|
201
|
+
* so one in-flight call per purpose is tracked under the resilient-call slice —
|
|
202
|
+
* the common single-call-per-stage shape. A consumer that fans out many calls
|
|
203
|
+
* of one purpose passes a distinct `key`.
|
|
204
|
+
*/
|
|
205
|
+
interface LlmCall<P extends string> {
|
|
206
|
+
/** The stage / schema selector — drives both `schemas[purpose]` and message assembly. */
|
|
207
|
+
readonly purpose: P;
|
|
208
|
+
/** The model id to invoke; `null` = the host's default model. */
|
|
209
|
+
readonly model: string | null;
|
|
210
|
+
/** The per-purpose prompt payload the `MessageLoader` consumes. Opaque to the knob. */
|
|
211
|
+
readonly payload: unknown;
|
|
212
|
+
}
|
|
213
|
+
/** The parsed, typed success carried on the `resilient_ok` settle Msg, tagged with its purpose. */
|
|
214
|
+
interface LlmOk<P extends string, O extends Record<P, unknown>> {
|
|
215
|
+
readonly key: string;
|
|
216
|
+
readonly purpose: P;
|
|
217
|
+
readonly output: O[P];
|
|
218
|
+
}
|
|
219
|
+
/** The typed failure variant — every failure path surfaces this, tagged by purpose. */
|
|
220
|
+
interface LlmErr<P extends string> {
|
|
221
|
+
readonly key: string;
|
|
222
|
+
readonly purpose: P;
|
|
223
|
+
/** A human-readable cause (model throw, retry-exhaustion, or schema parse). */
|
|
224
|
+
readonly reason: string;
|
|
225
|
+
/** The original error, carried untouched for the consumer to inspect. */
|
|
226
|
+
readonly error: unknown;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The settle Msgs llm-call's handler RETURNS from `interpret` so the substrate
|
|
230
|
+
* enqueues them as follow-up Msgs (re-entry) into the host reducer — exactly as
|
|
231
|
+
* `../resilient-call` does. They are the resilient-call settle Msgs with the
|
|
232
|
+
* payloads ENRICHED to llm-call's typed variants: `result` is the parsed,
|
|
233
|
+
* purpose-tagged `LlmOk`; `error` is the typed `LlmErr` (purpose + reason +
|
|
234
|
+
* raw). The host wires `resilient_ok` → `succeed` and `resilient_err` → `fail`
|
|
235
|
+
* reducer arms (the doc-comment wiring), and folds the enriched payload into its
|
|
236
|
+
* own state there — llm-call never forces a Msg vocabulary on the host, and the
|
|
237
|
+
* settle Msg drives the inherited succeed/fail → backoff → onTimer loop instead
|
|
238
|
+
* of bypassing it.
|
|
239
|
+
*/
|
|
240
|
+
type LlmSucceedMsg<P extends string, O extends Record<P, unknown>> = SucceedMsg<LlmOk<P, O>>;
|
|
241
|
+
/**
|
|
242
|
+
* The FAILURE settle Msg (`resilient_err`) — resilient-call's `FailMsg` with its
|
|
243
|
+
* `error: unknown` narrowed to the typed `LlmErr`, so the host reducer reads the
|
|
244
|
+
* purpose, the reason and the raw payload without a cast.
|
|
245
|
+
*/
|
|
246
|
+
type LlmFailMsg<P extends string> = Omit<FailMsg, "error"> & {
|
|
247
|
+
readonly error: LlmErr<P>;
|
|
248
|
+
};
|
|
249
|
+
/** The retry / deadline timer Msg — `DeadlineExceeded`, inherited from resilient-call. */
|
|
250
|
+
type LlmTimerMsg = DeadlineExceeded;
|
|
251
|
+
/**
|
|
252
|
+
* The ports the LEGACY detached `handlers(ports)` form takes. Two outbound Msg
|
|
253
|
+
* builders the handler hands the typed Ok / Err; the consumer returns its own
|
|
254
|
+
* Msg (or `undefined`). KEPT only for `../agent`, which still inherits the
|
|
255
|
+
* detached shape; that path does NOT drive the retry loop (it dispatches the
|
|
256
|
+
* consumer's Msg directly and never re-enters the resilient settle Msg). New
|
|
257
|
+
* consumers use the no-arg `handlers()` form, which RETURNS the settle Msg for
|
|
258
|
+
* re-entry and drives the inherited succeed/fail → backoff → onTimer loop.
|
|
259
|
+
* `../agent` carries the same retry-loop gap and is fixed separately.
|
|
260
|
+
*
|
|
261
|
+
* The builders return `M | undefined` — `undefined` is the "dispatch nothing"
|
|
262
|
+
* sentinel the detached form checks (`!== undefined`), spelled as `undefined`
|
|
263
|
+
* rather than `void` so the union is unambiguous (a `void` member of a union is
|
|
264
|
+
* confusing and reads as "may return anything").
|
|
265
|
+
*/
|
|
266
|
+
interface LlmCallPorts<P extends string, O extends Record<P, unknown>, M> {
|
|
267
|
+
/** Build the Msg dispatched on a parsed success. Return `undefined` to dispatch nothing. */
|
|
268
|
+
readonly onOk: (ok: LlmOk<P, O>) => M | undefined;
|
|
269
|
+
/** Build the Msg dispatched on any failure (throw / retry-exhausted / parse). */
|
|
270
|
+
readonly onErr: (err: LlmErr<P>) => M | undefined;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The effect Cmd this module emits: run the LLM call for `key` with `input`. The
|
|
274
|
+
* `input` is the plain `LlmCall` request — the handler reads `purpose` /
|
|
275
|
+
* `model` / `payload` off it (invariant 3: Cmds are data).
|
|
276
|
+
*
|
|
277
|
+
* It is `../resilient-call`'s `RunCmd` specialized to the `LlmCall` input — NOT a
|
|
278
|
+
* re-declared shape — so the resilient verbs' return tuples (`[State, RunCmd[]]`)
|
|
279
|
+
* are this exact type and thread through `attempt` / `succeed` / `fail` /
|
|
280
|
+
* `onTimer` with no cast.
|
|
281
|
+
*/
|
|
282
|
+
type LlmRunCmd<P extends string> = RunCmd<LlmCall<P>>;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @demlik/tea/agent — the domain data model: the tool + turn shapes the consumer
|
|
286
|
+
* supplies, the agentic-stage conversation, the `createAgent` config object, and
|
|
287
|
+
* the durable agent slice + its lifecycle status.
|
|
288
|
+
*
|
|
289
|
+
* Everything here is plain data (durable + replayable): the composed wrappers'
|
|
290
|
+
* slices, the conversation, and the config are all JSON-serializable. The
|
|
291
|
+
* compaction vocabulary lives in `./compaction`; this module imports its
|
|
292
|
+
* types where the slice / config reference them. The reducer core (the verbs +
|
|
293
|
+
* the wired `toMachine`) lives in `./index`.
|
|
294
|
+
*/
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* One tool the model asked to call this turn. Plain data so it round-trips through the
|
|
298
|
+
* durable fan-out ledger: a stable `callId` (the fan-out identity), the tool
|
|
299
|
+
* `name`, and the opaque `args` the consumer's own interpret reads.
|
|
300
|
+
*/
|
|
301
|
+
interface ToolCall {
|
|
302
|
+
/** Stable id the model minted; the fan-out item identity (`idOf`). */
|
|
303
|
+
readonly callId: string;
|
|
304
|
+
/** The tool to invoke. The consumer's `toolOf` maps it to an effect Cmd. */
|
|
305
|
+
readonly name: string;
|
|
306
|
+
/** Args the model emitted, opaque to the agent. */
|
|
307
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* One model turn: the narration `content` the model produced and the
|
|
311
|
+
* `toolCalls` it asked us to run. An empty
|
|
312
|
+
* `toolCalls` means the model is done with this stage (advance the pipeline).
|
|
313
|
+
* This is the parsed output of a brain call; the consumer's schema produces it.
|
|
314
|
+
*
|
|
315
|
+
* `provider` is the opaque passthrough slot (#93): whatever the adapter hands
|
|
316
|
+
* back beside `content` / `toolCalls` that its provider expects echoed
|
|
317
|
+
* verbatim on the next turn — Anthropic's signed `thinking` blocks,
|
|
318
|
+
* `server_tool_use`, OpenAI reasoning items. tea stores it on the turn and
|
|
319
|
+
* returns it unchanged on the `assistant` `AgentMessage` that replays this
|
|
320
|
+
* turn; it never reads, validates or transforms it. The adapter owns its shape,
|
|
321
|
+
* which must be JSON-serializable like the rest of the Model. A compaction fold
|
|
322
|
+
* (ADR 0004) summarises turns into text, so it drops the slot with the turns it
|
|
323
|
+
* summarises. Optional and additive: a persisted turn without it still parses.
|
|
324
|
+
*/
|
|
325
|
+
interface AgentTurn {
|
|
326
|
+
/** Free-text narration the model produced this turn (folded into the conversation). */
|
|
327
|
+
readonly content: string;
|
|
328
|
+
/** The tools the model asked us to run; empty = stage done. */
|
|
329
|
+
readonly toolCalls: readonly ToolCall[];
|
|
330
|
+
/** Provider-opaque blocks to echo back verbatim next turn. tea never reads it. */
|
|
331
|
+
readonly provider?: unknown;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* One partial piece of a turn the model is still producing — a token delta, as
|
|
335
|
+
* the provider emitted it.
|
|
336
|
+
*
|
|
337
|
+
* A chunk is NOT state. It never reaches the Model, the journal or the Store:
|
|
338
|
+
* by the time a turn is durable it has settled, and a delta that has not settled
|
|
339
|
+
* is not an outcome ADR 0011 could fold. So `TurnChunk` carries only what a
|
|
340
|
+
* progress surface renders — the text so far — and carries no identity, because
|
|
341
|
+
* an identity would invite someone to store it.
|
|
342
|
+
*/
|
|
343
|
+
interface TurnChunk {
|
|
344
|
+
/** The text delta this chunk adds to the turn's `content`. */
|
|
345
|
+
readonly text: string;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* The side channel a {@link StreamingModel} writes its deltas to — the second
|
|
349
|
+
* argument of the streaming port. One sink, called synchronously by the adapter
|
|
350
|
+
* as the provider yields; a throw from it is contained by the caller that
|
|
351
|
+
* supplied it, so a progress surface's defect cannot fail a model call.
|
|
352
|
+
*/
|
|
353
|
+
interface ModelStream {
|
|
354
|
+
/** Hand one delta to whoever is watching this run. */
|
|
355
|
+
readonly onChunk: (chunk: TurnChunk) => void;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* The streaming model port — `(messages, { onChunk }) => Promise<AgentTurn>`.
|
|
359
|
+
*
|
|
360
|
+
* The second, OPTIONAL shape of the brain. It differs from
|
|
361
|
+
* `PlainModel<Msg, T>` in one thing: it is handed a {@link ModelStream} it may
|
|
362
|
+
* write deltas to while the turn is in flight. What it RESOLVES is the same
|
|
363
|
+
* settled turn the plain port resolves, and that turn is the only thing the
|
|
364
|
+
* reducer ever sees — the deltas are a side channel that ends at the caller's
|
|
365
|
+
* listener.
|
|
366
|
+
*
|
|
367
|
+
* A `PlainModel` is assignable here (a function may declare fewer parameters
|
|
368
|
+
* than its type), which is what lets one config field accept either shape.
|
|
369
|
+
* `isStreamingModel` tells them apart at runtime so a plain model is still
|
|
370
|
+
* invoked with exactly one argument, as it always was.
|
|
371
|
+
*/
|
|
372
|
+
type StreamingModel<Msg, T = unknown> = (messages: readonly Msg[], stream: ModelStream) => Promise<T>;
|
|
373
|
+
/**
|
|
374
|
+
* Whether a model port wants the {@link ModelStream} — read off its declared
|
|
375
|
+
* arity, which is the mark JavaScript already carries. No config flag, and no
|
|
376
|
+
* marker property for a consumer to forget: a brain written
|
|
377
|
+
* `async (messages, { onChunk }) => …` streams, and one written
|
|
378
|
+
* `async (messages) => …` does not.
|
|
379
|
+
*
|
|
380
|
+
* The cost of the arity read is the cost of every arity read — a model that
|
|
381
|
+
* declares a parameter it ignores, or takes `...args`, reads as the other shape.
|
|
382
|
+
* Both misreads are benign: a `...args` model is handed no stream and emits no
|
|
383
|
+
* chunks; a model with an unused second parameter is handed a stream it never
|
|
384
|
+
* writes to. Neither can change the settled turn. PURE.
|
|
385
|
+
*/
|
|
386
|
+
declare function isStreamingModel(model: (...args: never[]) => unknown): boolean;
|
|
387
|
+
/**
|
|
388
|
+
* Narrow an unknown to an `AgentTurn` — the runtime witness for tea's own
|
|
389
|
+
* structured-output type. A consumer's brain schema parses the model's output
|
|
390
|
+
* into an `AgentTurn` (the agentic purpose's output); rather than every consumer
|
|
391
|
+
* hand-rolling this guard + a `Schema<AgentTurn>` for a type the agent OWNS, the
|
|
392
|
+
* agent exports both. Checks the load-bearing fields: `content` is a string and
|
|
393
|
+
* `toolCalls` is an array of `ToolCall` (each element guarded — the narrow is a
|
|
394
|
+
* real parse of the boundary, not a shallow `Array.isArray`). `provider` is
|
|
395
|
+
* opaque, so its presence or absence is not a fact the guard reads. PURE —
|
|
396
|
+
* allocates no Error.
|
|
397
|
+
*/
|
|
398
|
+
declare function isAgentTurn(value: unknown): value is AgentTurn;
|
|
399
|
+
/**
|
|
400
|
+
* The `Schema<AgentTurn>` for tea's own turn type — the parse target a brain
|
|
401
|
+
* call binds when the agentic purpose's output is a bare `AgentTurn` (the common
|
|
402
|
+
* case). Throws on a non-`AgentTurn` (the zod-style `parse` contract the
|
|
403
|
+
* llm-call handler relies on). Deletes the consumer's hand-rolled
|
|
404
|
+
* `auditSchema` / `isAgentTurn` pair.
|
|
405
|
+
*
|
|
406
|
+
* A consumer whose turn type EXTENDS `AgentTurn` with extra fields still writes
|
|
407
|
+
* its own schema (it must validate the extra fields); this is for the plain case.
|
|
408
|
+
*/
|
|
409
|
+
declare const agentTurnSchema: Schema<AgentTurn>;
|
|
410
|
+
/**
|
|
411
|
+
* One settled tool outcome the consumer routes back into the loop.
|
|
412
|
+
* `ok` carries the result the consumer's interpret produced;
|
|
413
|
+
* `error` carries the failure as data — see {@link ToolFailure}.
|
|
414
|
+
*/
|
|
415
|
+
type ToolOutcome<R> = {
|
|
416
|
+
readonly kind: "ok";
|
|
417
|
+
readonly result: R;
|
|
418
|
+
} | ToolFailure;
|
|
419
|
+
/**
|
|
420
|
+
* A settled tool failure as the conversation keeps it: the `{ _tag, …payload }`
|
|
421
|
+
* the tool failed with, spread beside the `reason` string the model reads.
|
|
422
|
+
*
|
|
423
|
+
* Both halves are load-bearing and neither replaces the other. `reason` is the
|
|
424
|
+
* model's channel — `toolErrorReason`'s rendering, which is what the next brain
|
|
425
|
+
* call carries; `_tag` and the payload are the program's, so host code can
|
|
426
|
+
* branch on the failure instead of parsing prose out of `reason` (#115). A
|
|
427
|
+
* payload field never shadows `kind` or `reason`: the router writes those last.
|
|
428
|
+
*
|
|
429
|
+
* `_tag` is OPTIONAL here for one reason only — a record persisted by 0.12.x or
|
|
430
|
+
* earlier was written before the tag was kept, so a Store handing one back has
|
|
431
|
+
* a failure with no tag, and a required field would be a lie about durable
|
|
432
|
+
* state. Every failure THIS version mints carries one. For the exhaustive
|
|
433
|
+
* per-tag union, take `onToolError`'s argument, which is typed from the tools
|
|
434
|
+
* themselves ({@link ToolFailureOf}).
|
|
435
|
+
*/
|
|
436
|
+
interface ToolFailure {
|
|
437
|
+
readonly kind: "error";
|
|
438
|
+
/** The model-facing rendering — `toolErrorReason(error)`. */
|
|
439
|
+
readonly reason: string;
|
|
440
|
+
/** The failure's tag; absent only on a record persisted before 0.13. */
|
|
441
|
+
readonly _tag?: string;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* The failure arm typed against a KNOWN tag union — `{ kind, reason }` beside
|
|
445
|
+
* each arm of `E`, distributed, so a `switch` on `_tag` narrows the payload and
|
|
446
|
+
* an unhandled tag is a compile error. `ToolFailure` is its erased form.
|
|
447
|
+
*/
|
|
448
|
+
type TaggedFailure<E extends Tagged> = E extends unknown ? {
|
|
449
|
+
readonly kind: "error";
|
|
450
|
+
readonly reason: string;
|
|
451
|
+
} & E : never;
|
|
452
|
+
/**
|
|
453
|
+
* The per-tool resilience knob — a timeout, a retry ladder, or both, declared on
|
|
454
|
+
* the `tool()` spec and executed by the agent's reducer through
|
|
455
|
+
* `../internal/resilience/resilient-call`.
|
|
456
|
+
*
|
|
457
|
+
* Both fields are optional and a tool that names neither behaves exactly as it
|
|
458
|
+
* did before this knob existed: no slice entry is minted, no timer is armed, and
|
|
459
|
+
* its settle path is the plain fan-out one. The ladder lives in the Model, so a
|
|
460
|
+
* process killed between two attempts resumes at the attempt it was on rather
|
|
461
|
+
* than restarting the run — the property a `Promise.race` or a `for` loop inside
|
|
462
|
+
* the handler cannot have, because those live inside the effect boundary.
|
|
463
|
+
*/
|
|
464
|
+
interface ToolResilience {
|
|
465
|
+
/**
|
|
466
|
+
* The budget one tool CALL gets, in ms, measured from the first attempt and
|
|
467
|
+
* NOT restarted by a retry — the overall cap `resilient-call`'s deadline brick
|
|
468
|
+
* enforces. When it elapses the call settles as a `ToolOutcome` error
|
|
469
|
+
* carrying `_tag: "timeout"` beside its `reason`, and the loop moves on, whatever the
|
|
470
|
+
* in-flight attempt does next — its late settle arrives for a call nothing is
|
|
471
|
+
* waiting on and folds nothing, so a slow tool costs the budget and not the
|
|
472
|
+
* turn.
|
|
473
|
+
*
|
|
474
|
+
* What it does NOT do is cancel the handler: a promise cannot be cancelled in
|
|
475
|
+
* JavaScript, so the attempt runs to its own end, and `run`'s teardown still
|
|
476
|
+
* drains it. A handler that resolves LATE is bounded by this knob; a handler
|
|
477
|
+
* that never resolves at all holds the runtime's shutdown regardless, and
|
|
478
|
+
* wants an `AbortSignal` in the handler rather than a budget out here.
|
|
479
|
+
*
|
|
480
|
+
* Omit → the call has no cap and ends only when its handler settles.
|
|
481
|
+
*/
|
|
482
|
+
readonly timeoutMs?: number;
|
|
483
|
+
/**
|
|
484
|
+
* The backoff ladder a FAILED attempt climbs — the same
|
|
485
|
+
* `BackoffCurve & RetryBudget` shape `AgentConfigCore.retry` takes for the
|
|
486
|
+
* brain call. Each failure is recorded in the Model and the next attempt is
|
|
487
|
+
* armed as a timer Sub, so the wait is durable and the attempt count survives
|
|
488
|
+
* a reload. When the budget is spent the call settles as a `ToolOutcome` error
|
|
489
|
+
* carrying `_tag: "retry_exhausted"` beside its `reason`, plus `attempts` and
|
|
490
|
+
* `last` as fields — see {@link ToolRetryExhausted}.
|
|
491
|
+
*
|
|
492
|
+
* Omit → the first failure is the outcome, exactly as before.
|
|
493
|
+
*/
|
|
494
|
+
readonly retry?: AnyRetryPolicy;
|
|
495
|
+
}
|
|
496
|
+
/** The reason-tag a timed-out tool call settles under. */
|
|
497
|
+
declare const TOOL_TIMEOUT_TAG = "timeout";
|
|
498
|
+
/** The reason-tag a tool call that spent its retry budget settles under. */
|
|
499
|
+
declare const TOOL_RETRY_EXHAUSTED_TAG = "retry_exhausted";
|
|
500
|
+
/** A call that spent its `timeoutMs` budget — {@link ToolResilience.timeoutMs}. */
|
|
501
|
+
interface ToolTimedOut {
|
|
502
|
+
readonly _tag: typeof TOOL_TIMEOUT_TAG;
|
|
503
|
+
}
|
|
504
|
+
/** A call that spent its retry budget — {@link ToolResilience.retry}. */
|
|
505
|
+
interface ToolRetryExhausted {
|
|
506
|
+
readonly _tag: typeof TOOL_RETRY_EXHAUSTED_TAG;
|
|
507
|
+
/** How many attempts the ladder burned, the failed last one included. */
|
|
508
|
+
readonly attempts: number;
|
|
509
|
+
/** The last attempt's own `reason`, so the real failure is not buried. */
|
|
510
|
+
readonly last: string;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* The two failures the resilience ladder itself authors — `timeout` and
|
|
514
|
+
* `retry_exhausted`. They are in every tool's
|
|
515
|
+
* error union rather than only a policied tool's: the policy is a `tool()` spec
|
|
516
|
+
* field a maintainer can add later, and a union that narrowed with it would turn
|
|
517
|
+
* adding a `timeoutMs` into a silent behaviour change at every `onToolError`
|
|
518
|
+
* that had switched exhaustively without them.
|
|
519
|
+
*/
|
|
520
|
+
type ToolResilienceError = ToolTimedOut | ToolRetryExhausted;
|
|
521
|
+
/**
|
|
522
|
+
* A folded tool record kept on the conversation once a tool settles — the
|
|
523
|
+
* call + its outcome, in settle order. The consumer's message loader reads
|
|
524
|
+
* these (plus `turns`) to assemble the next brain call's prompt.
|
|
525
|
+
*
|
|
526
|
+
* `turn` is the `turnCount` at the time the record was folded — the association
|
|
527
|
+
* compaction needs to drop the RIGHT records (#85 / design A1). When compaction
|
|
528
|
+
* folds `turns[0..N]` into a summary, it must also drop every tool record that
|
|
529
|
+
* belonged to those turns; without a turn stamp on the record there is no honest
|
|
530
|
+
* way to know which. It is the round-trip index, not a wall clock — small and
|
|
531
|
+
* JSON-stable, preserving the durability invariant.
|
|
532
|
+
*/
|
|
533
|
+
interface ToolRecord<R> {
|
|
534
|
+
readonly call: ToolCall;
|
|
535
|
+
readonly outcome: ToolOutcome<R>;
|
|
536
|
+
/** The `turnCount` at fold time — which round-trip this record belongs to (#85, A1). */
|
|
537
|
+
readonly turn: number;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Whether the agentic stage is waiting on the model (`llm`), on tools
|
|
541
|
+
* (`tools`), or on a compaction round-trip (`compacting`). The in-flight
|
|
542
|
+
* fan-out batch exists ONLY in the `tools` variant — "awaiting llm with tools
|
|
543
|
+
* pending" is structurally impossible (TEA canon §2.6 / Rule 1
|
|
544
|
+
* impossible-states pin). Discriminated on `kind`.
|
|
545
|
+
*
|
|
546
|
+
* `compacting` is the honest state for the dedicated compaction round-trip
|
|
547
|
+
* (design B1): the transcript grew, the policy asked to fold `folding` of the
|
|
548
|
+
* OLDEST turns, and the summarize call is in flight INSTEAD of the next brain
|
|
549
|
+
* call. `folding` is the count `planCompaction` returned — the fold-back
|
|
550
|
+
* (`compact_ok`) drops exactly that many head turns + their tool records.
|
|
551
|
+
*/
|
|
552
|
+
type Awaiting = {
|
|
553
|
+
readonly kind: "llm";
|
|
554
|
+
} | {
|
|
555
|
+
readonly kind: "tools";
|
|
556
|
+
readonly batchTurn: number;
|
|
557
|
+
} | {
|
|
558
|
+
readonly kind: "compacting";
|
|
559
|
+
readonly folding: number;
|
|
560
|
+
};
|
|
561
|
+
/**
|
|
562
|
+
* The agentic-stage conversation — durable inside the agent slice so an
|
|
563
|
+
* eviction mid-loop resumes the exact turn. The model turns + folded tool
|
|
564
|
+
* records are the consumer's prompt source (via the llm-call message loader);
|
|
565
|
+
* `turnCount` drives the livelock guard; `awaiting` carries the ONE outstanding
|
|
566
|
+
* effect for the agentic stage.
|
|
567
|
+
*
|
|
568
|
+
* Generic over the consumer's tool-result type `R` (what `toolOk` carries).
|
|
569
|
+
*/
|
|
570
|
+
interface Conversation<R> {
|
|
571
|
+
/** Model turns this stage has produced, in order. */
|
|
572
|
+
readonly turns: readonly AgentTurn[];
|
|
573
|
+
/** Settled tool records, in settle order — the model sees these next turn. */
|
|
574
|
+
readonly toolRecords: readonly ToolRecord<R>[];
|
|
575
|
+
/** Monotonic round-trip count; the livelock guard compares it to `maxTurns`. */
|
|
576
|
+
readonly turnCount: number;
|
|
577
|
+
/** What the loop is waiting for next. */
|
|
578
|
+
readonly awaiting: Awaiting;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The snapshotting discriminant. Checkpointing is either OFF — in which
|
|
582
|
+
* case `snapshotEvery` is structurally absent and the monitored-run slice never
|
|
583
|
+
* emits a `snapshot_write` Cmd — or ON, in which case `snapshotEvery` is a
|
|
584
|
+
* `number`. A `{ snapshotEvery?: never }` member (rather than a bare optional)
|
|
585
|
+
* makes the OFF case load-bearing: it forbids passing `snapshotEvery` at all, so
|
|
586
|
+
* `toMachine` can config-derive whether the `snapshot_write` interpret handler
|
|
587
|
+
* is REQUIRED (ON) or FORBIDDEN (OFF) instead of defaulting it to a silent
|
|
588
|
+
* no-op.
|
|
589
|
+
*
|
|
590
|
+
* This is what kills the type lie: with checkpointing off, `snapshot_write` is
|
|
591
|
+
* absent from the consumer's interpret contract entirely — never a
|
|
592
|
+
* `snapshot_write: async () => undefined` ceremony that masks a real wiring bug.
|
|
593
|
+
*/
|
|
594
|
+
type AgentSnapshotConfig = {
|
|
595
|
+
readonly snapshotEvery?: never;
|
|
596
|
+
} | {
|
|
597
|
+
readonly snapshotEvery: number;
|
|
598
|
+
};
|
|
599
|
+
/**
|
|
600
|
+
* The core (non-snapshot, non-compaction) agent configuration. The full `AgentConfig`
|
|
601
|
+
* intersects this with the `AgentSnapshotConfig` + `AgentCompactionConfig`
|
|
602
|
+
* discriminants — see those types for why the cadence / policy are discriminated
|
|
603
|
+
* unions rather than bare optionals.
|
|
604
|
+
*
|
|
605
|
+
* Type parameters:
|
|
606
|
+
* - `Stage` — the consumer's pipeline stage id (any JSON value).
|
|
607
|
+
* - `P` — the brain-call purpose union (`"plan" | "act" | ...`).
|
|
608
|
+
* - `O` — the purpose→output map. Every purpose drives the agentic loop,
|
|
609
|
+
* so each output is pinned to `AgentTurn` (or a subtype) by the
|
|
610
|
+
* `O extends Record<P, AgentTurn>` bound — that is what the loop
|
|
611
|
+
* folds. An `Outputs` whose purpose output is NOT an `AgentTurn`
|
|
612
|
+
* fails to compile here; the rule lives in the type, not a cast.
|
|
613
|
+
* - `R` — the consumer's tool-result type (what `toolOk` carries).
|
|
614
|
+
* - `TC` — the consumer's per-tool effect Cmd, the discriminated variant
|
|
615
|
+
* `toolOf` produces. Kept PRECISE (a closed Cmd variant, not the
|
|
616
|
+
* open `Cmd`) so `AgentCmd<P, TC>` stays a closed discriminated
|
|
617
|
+
* union and the wired machine's interpret merge type-checks per
|
|
618
|
+
* key — the brain handler maps to `resilient_run`, the tool one to
|
|
619
|
+
* `TC["type"]` — with no laundering cast. Defaults to `Cmd` for
|
|
620
|
+
* the consumer that does not care to name its tool Cmd.
|
|
621
|
+
* - `Msg` — the model's message shape (threaded through the llm-call loader).
|
|
622
|
+
*/
|
|
623
|
+
interface AgentConfigCore<Stage, P extends string, O extends Record<P, AgentTurn>, R, TC extends Cmd = Cmd, Msg = unknown> {
|
|
624
|
+
/** The ordered stages. Omit / empty → a single-shot run (one agentic stage). */
|
|
625
|
+
readonly stages?: readonly Stage[];
|
|
626
|
+
/** No-progress watchdog budget, in ms. Omit → no watchdog. */
|
|
627
|
+
readonly deadlineMs?: number;
|
|
628
|
+
/**
|
|
629
|
+
* DI port — the brain. `async (messages) => turn` is the common path:
|
|
630
|
+
* the returned turn is validated through the purpose's schema
|
|
631
|
+
* (`agentTurnSchema` for the plain case), so a malformed turn is the run's
|
|
632
|
+
* `llm` failure, not a throw. `(modelId) => Llm` is the advanced form for a
|
|
633
|
+
* model that binds the structured-output schema itself.
|
|
634
|
+
*/
|
|
635
|
+
readonly model: ModelPort<Msg, O[P]>;
|
|
636
|
+
/**
|
|
637
|
+
* The system prompt, stored ON THE MODEL at `init` (`AgentState.instructions`)
|
|
638
|
+
* rather than closed over — so a replay reproduces the exact prompt that ran,
|
|
639
|
+
* a rehydrated run keeps the prompt it started with, and a compaction fold
|
|
640
|
+
* (which touches only the conversation) can never lose it (ADR 0004). It
|
|
641
|
+
* reaches the brain call through `payloadOf`'s third argument. Omit → `null`.
|
|
642
|
+
*/
|
|
643
|
+
readonly instructions?: string;
|
|
644
|
+
/** One structured-output schema per purpose; the parse target per brain call. */
|
|
645
|
+
readonly schemas: {
|
|
646
|
+
readonly [K in P]: Schema<O[K]>;
|
|
647
|
+
};
|
|
648
|
+
/** Backoff policy for brain calls, composed into `../llm-call`. Omit → no backoff. */
|
|
649
|
+
readonly retry?: RetryPolicy;
|
|
650
|
+
/** DI port — the SDK / message loader. Omit → brain calls invoke with `[]`. */
|
|
651
|
+
readonly loadMessages?: MessageLoader<P, Msg>;
|
|
652
|
+
/** Map one tool call to the effect Cmd the consumer's interpret performs. */
|
|
653
|
+
readonly toolOf: (call: ToolCall) => TC;
|
|
654
|
+
/**
|
|
655
|
+
* The per-tool timeout / retry knob for a call, read once per launch. `null`
|
|
656
|
+
* (or an omitted seam) → the tool runs bare and its settle path is the plain
|
|
657
|
+
* fan-out one, byte for byte what it was before the knob existed.
|
|
658
|
+
*
|
|
659
|
+
* This is the seam a `toolRouter` fills from the `tool()` specs
|
|
660
|
+
* (`router.resilienceOf`), which is how `defineAgent` grows the knob without
|
|
661
|
+
* growing a lid option: the policy is declared where the tool is. A hand-wired
|
|
662
|
+
* `createAgent` supplies it directly. It must be PURE and config-derived — the
|
|
663
|
+
* reducer calls it on the launch path.
|
|
664
|
+
*/
|
|
665
|
+
readonly toolResilienceOf?: (call: ToolCall) => ToolResilience | null;
|
|
666
|
+
/**
|
|
667
|
+
* How many of ONE turn's tool calls a transition LAUNCHES at once. At the
|
|
668
|
+
* default `1` a turn's calls go out one at a time; raise it and up to that
|
|
669
|
+
* many move from `pending` to `running` in the fan-out ledger in a single
|
|
670
|
+
* transition, so the durable Model (and a `store`'s record of it) says what
|
|
671
|
+
* was launched.
|
|
672
|
+
*
|
|
673
|
+
* Above `1` those calls also OVERLAP on the clock, so a turn of two slow
|
|
674
|
+
* tools costs the slower rather than the sum — but only for the tool cells a
|
|
675
|
+
* `toolRouter` owns (the `toMachine({ tools })` path, which `defineAgent`
|
|
676
|
+
* takes). The overlap lives inside those cells, never in the kernel:
|
|
677
|
+
* `runInterpret` still interprets a transition's Cmds one after another and
|
|
678
|
+
* never interleaves two handlers (ADR 0018). A hand-wired `toolInterpret`
|
|
679
|
+
* cell is yours, so it overlaps only if you write it to.
|
|
680
|
+
*
|
|
681
|
+
* Overlapping does NOT reorder the fold. Settle Msgs are dispatched in
|
|
682
|
+
* Cmd-EMISSION order whichever call finishes first, so a replayed log
|
|
683
|
+
* reproduces the same Model — invariant 2's serializability, which the knob
|
|
684
|
+
* was never allowed to spend.
|
|
685
|
+
*
|
|
686
|
+
* Omit (or `1`) → serial dispatch, exactly as before.
|
|
687
|
+
*/
|
|
688
|
+
readonly toolConcurrency?: number;
|
|
689
|
+
/**
|
|
690
|
+
* Which brain-call purpose a given stage runs. The agent fires
|
|
691
|
+
* `call_llm{ turnOf(stage) }` to drive the agentic stage's loop. The purpose's
|
|
692
|
+
* schema output is an `AgentTurn` — enforced by the `O extends Record<P,
|
|
693
|
+
* AgentTurn>` bound, not left to a doc-comment.
|
|
694
|
+
*/
|
|
695
|
+
readonly turnOf: (stage: Stage | undefined) => P;
|
|
696
|
+
/**
|
|
697
|
+
* Build the per-purpose brain-call payload from the durable state: the
|
|
698
|
+
* stage, the conversation, and the Model's `instructions` slot. Omit → `null`.
|
|
699
|
+
*/
|
|
700
|
+
readonly payloadOf?: (stage: Stage | undefined, conversation: Conversation<R>, instructions: string | null) => unknown;
|
|
701
|
+
/** The model id every brain call invokes. Omit → `null` (the host's default). */
|
|
702
|
+
readonly modelId?: string | null;
|
|
703
|
+
/**
|
|
704
|
+
* Livelock guard: bound on model round-trips within one run. On
|
|
705
|
+
* `turnCount >= maxTurns` the run fails `{ reason: "turn_limit" }`. Omit → no
|
|
706
|
+
* turn guard (the other stop conditions still bound the loop).
|
|
707
|
+
*/
|
|
708
|
+
readonly maxTurns?: number;
|
|
709
|
+
/**
|
|
710
|
+
* Wall-clock guard: total milliseconds from `run.startedAt` the run may take.
|
|
711
|
+
* At the turn boundary, `at - run.startedAt >= maxElapsedMs` fails the run
|
|
712
|
+
* `{ reason: "elapsed_limit" }`. Unlike `deadlineMs` — a no-progress watchdog
|
|
713
|
+
* that RESTARTS on every advance — this budget never restarts, so a run that
|
|
714
|
+
* keeps progressing is bounded by it. Omit → no wall-clock cap.
|
|
715
|
+
*
|
|
716
|
+
* `startedAt` is durable, so a resumed run continues the ORIGINAL budget
|
|
717
|
+
* rather than starting a fresh one; the elapsed span a killed run spent dead
|
|
718
|
+
* counts against it.
|
|
719
|
+
*/
|
|
720
|
+
readonly maxElapsedMs?: number;
|
|
721
|
+
/**
|
|
722
|
+
* The consumer's own stop condition, consulted at the turn boundary once the
|
|
723
|
+
* turn-count and wall-clock guards have passed. `true` settles the run
|
|
724
|
+
* `cancelled` at `at` — the same terminal an aborted `signal` reaches, so the
|
|
725
|
+
* transcript stands and no further model call goes out. Omit → no predicate.
|
|
726
|
+
*
|
|
727
|
+
* Where `maxTurns` and `maxElapsedMs` bound a quantity this agent counts,
|
|
728
|
+
* this bounds one only the caller can see (a token ledger, an external flag,
|
|
729
|
+
* a condition on the turns so far). It must be PURE and total over the state
|
|
730
|
+
* it is handed — the reducer calls it, so a replay of the same Msg log calls
|
|
731
|
+
* it with the same state and must get the same answer. It is config, not
|
|
732
|
+
* Model: a resumed run consults the predicate the config passed to THIS boot.
|
|
733
|
+
*/
|
|
734
|
+
readonly stopWhen?: (state: AgentState<Stage, P, O, R>) => boolean;
|
|
735
|
+
/**
|
|
736
|
+
* The impurity-injection seam for the inherited brain-call retry jitter — the
|
|
737
|
+
* ONE place this otherwise-pure machine reads randomness. Pass a fixed
|
|
738
|
+
* `() => 0` to pin backoff (tests, replay, durability proofs). Omit →
|
|
739
|
+
* `Math.random`, read only at the resilient-call verb boundary.
|
|
740
|
+
*/
|
|
741
|
+
readonly rng?: () => number;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* The agent configuration — the core seams intersected with the snapshotting discriminant
|
|
745
|
+
* (`AgentSnapshotConfig`). Type parameters are documented on `AgentConfigCore`;
|
|
746
|
+
* the only addition here is that `snapshotEvery` is the discriminant that drives
|
|
747
|
+
* whether `toMachine` requires (or forbids) the `snapshot_write` interpret
|
|
748
|
+
* handler.
|
|
749
|
+
*/
|
|
750
|
+
type AgentConfig<Stage, P extends string, O extends Record<P, AgentTurn>, R, TC extends Cmd = Cmd, Msg = unknown> = AgentConfigCore<Stage, P, O, R, TC, Msg> & AgentSnapshotConfig & AgentCompactionConfig<R, Msg>;
|
|
751
|
+
/**
|
|
752
|
+
* Why a run terminated as `failed`, beyond monitored-run's own reasons. The
|
|
753
|
+
* `turn_limit` reason is the agent's livelock guard and `elapsed_limit` its
|
|
754
|
+
* wall-clock one; deadline / stage failures surface through the monitored-run
|
|
755
|
+
* slice's own `failure`.
|
|
756
|
+
*/
|
|
757
|
+
type AgentFailure = {
|
|
758
|
+
readonly reason: "turn_limit";
|
|
759
|
+
readonly at: number;
|
|
760
|
+
} | {
|
|
761
|
+
readonly reason: "elapsed_limit";
|
|
762
|
+
readonly at: number;
|
|
763
|
+
} | {
|
|
764
|
+
readonly reason: "llm";
|
|
765
|
+
readonly error: unknown;
|
|
766
|
+
readonly at: number;
|
|
767
|
+
};
|
|
768
|
+
/**
|
|
769
|
+
* The agent slice — every composed wrapper's slice plus the loop's conversation
|
|
770
|
+
* and the agent-specific failure annotation.
|
|
771
|
+
*
|
|
772
|
+
* - `run` — the `../monitored-run` slice (pipeline position +
|
|
773
|
+
* lifecycle + watchdog). The OUTER lifecycle truth.
|
|
774
|
+
* - `resilience` — the `../llm-call` (= resilient-call) slice (brain-call
|
|
775
|
+
* retry / backoff). Per-purpose retry lives here.
|
|
776
|
+
* - `tools` — the `../fan-out` ledger for the current turn's tools.
|
|
777
|
+
* - `conversation` — the agentic-stage loop state. `null` until the loop is
|
|
778
|
+
* entered (the consumer seeds it at the agentic stage).
|
|
779
|
+
* - `failure` — the agent-specific terminal annotation (turn-limit /
|
|
780
|
+
* llm), null otherwise. Distinct from `run.failure`.
|
|
781
|
+
* - `instructions` — the system prompt this run was configured with, set at
|
|
782
|
+
* `init` and never touched by a fold (ADR 0004).
|
|
783
|
+
*/
|
|
784
|
+
interface AgentState<Stage, P extends string, O extends Record<P, unknown>, R> {
|
|
785
|
+
readonly run: MonitoredRunState<Stage>;
|
|
786
|
+
readonly resilience: ResilientState<LlmCall<P>, LlmOk<P, O>>;
|
|
787
|
+
readonly tools: FanOutState<ToolCall, ToolOutcome<R>>;
|
|
788
|
+
readonly conversation: Conversation<R> | null;
|
|
789
|
+
/**
|
|
790
|
+
* The compaction round-trip's resilient slice (#85, design B1) — a DEDICATED
|
|
791
|
+
* resilient-call slice for the reserved `$compact` purpose, separate from the
|
|
792
|
+
* brain `resilience` slice so a compaction retry/backoff never disturbs the
|
|
793
|
+
* brain call's breaker or retry counter. Always present (empty `calls` when no
|
|
794
|
+
* compaction is in flight, or when no policy is configured) so the slice stays
|
|
795
|
+
* a flat plain-data record — durable + replayable like every composed wrapper.
|
|
796
|
+
*/
|
|
797
|
+
readonly compaction: ResilientState<LlmCall<CompactionPurpose>, LlmOk<CompactionPurpose, CompactionOutputs>>;
|
|
798
|
+
/**
|
|
799
|
+
* The per-tool timeout / retry ladders (#117) — one dedicated resilient-call
|
|
800
|
+
* slice PER TOOL NAME, because the policy is declared per tool and one slice
|
|
801
|
+
* carries one config. Keyed by tool name; each slice's own keys are
|
|
802
|
+
* {@link toolCallKey}s, so two concurrent calls to the same tool climb
|
|
803
|
+
* independent ladders while sharing the tool's declared policy.
|
|
804
|
+
*
|
|
805
|
+
* A tool declaring neither `timeoutMs` nor `retry` mints no entry, so this is
|
|
806
|
+
* `{}` for every agent that uses no per-tool knob — the addition costs an
|
|
807
|
+
* empty record on the durable Model and nothing else.
|
|
808
|
+
*
|
|
809
|
+
* The result arm is `null`: a tool's VALUE settles through the fan-out ledger
|
|
810
|
+
* and the conversation, exactly as before. This slice tracks only the ladder —
|
|
811
|
+
* which attempt is out, when the next one is due, and when the budget is spent
|
|
812
|
+
* — which is what has to survive a reload.
|
|
813
|
+
*/
|
|
814
|
+
readonly toolResilience: Readonly<Record<string, ResilientState<ToolCall, null>>>;
|
|
815
|
+
/**
|
|
816
|
+
* The `callId`s whose PUBLIC outcome is already a failure (#145) — a tool the
|
|
817
|
+
* ladder settled on its deadline or on a spent retry budget, or one whose own
|
|
818
|
+
* error was folded. A promise cannot be cancelled, so the abandoned attempt
|
|
819
|
+
* may still resolve; when it does, its late `_ok` names a `callId` listed
|
|
820
|
+
* here and every public channel stays silent about it.
|
|
821
|
+
*
|
|
822
|
+
* The fan-out already drops the late value (its entry is gone, so the fold is
|
|
823
|
+
* a no-op), but "folds nothing" and "emits nothing" are two different facts:
|
|
824
|
+
* the event projector is Msg-shaped and would otherwise push a `ToolSettled`
|
|
825
|
+
* for a `callId` `onToolError` already reported as `{ _tag: "timeout" }`. The
|
|
826
|
+
* projector reads the post-transition state, by which point the ladder has
|
|
827
|
+
* forgotten the key, so the discriminator is carried HERE rather than derived
|
|
828
|
+
* from a presence check that erases both readings.
|
|
829
|
+
*
|
|
830
|
+
* Plain data, one string per failed call, cleared by `start` with the rest of
|
|
831
|
+
* the prior run's bookkeeping. It survives the batch drain and the stage
|
|
832
|
+
* retire on purpose — that is exactly when a late settle arrives.
|
|
833
|
+
*/
|
|
834
|
+
readonly refusedCalls: readonly string[];
|
|
835
|
+
readonly failure: AgentFailure | null;
|
|
836
|
+
/**
|
|
837
|
+
* The run's terminal output — the FIRST-CLASS result (issue #46). `null`
|
|
838
|
+
* until the pipeline finishes; set to the last model turn (the empty-tool
|
|
839
|
+
* turn that retired the final stage) the instant `run.phase` becomes `"done"`.
|
|
840
|
+
*
|
|
841
|
+
* This survives the `conversation` clear on stage retire: clearing the
|
|
842
|
+
* conversation is correct durability hygiene (a finished stage's transcript
|
|
843
|
+
* is not live state), but the run's PRODUCT is, so it lives here on the
|
|
844
|
+
* durable slice rather than being scraped off the `observe` firehose by
|
|
845
|
+
* matching the private `resilient_ok` Msg and racing the clear (the old
|
|
846
|
+
* `captureLastTurn` dance this field deletes). A consumer reads
|
|
847
|
+
* `runtime.result()?.output` / `(await runtime.done()).output`.
|
|
848
|
+
*
|
|
849
|
+
* Typed `AgentTurn | null` (not `O[P]`): the run's output is always a model
|
|
850
|
+
* turn — the `O extends Record<P, AgentTurn>` type bound pins every purpose's
|
|
851
|
+
* output to an `AgentTurn`, and the terminating turn is the one with no tool
|
|
852
|
+
* calls. The wider `Record<P, unknown>` bound on `AgentState` itself does not
|
|
853
|
+
* constrain `O[P]`, so naming the concrete `AgentTurn` keeps this field's type
|
|
854
|
+
* total without a purpose-indexing narrow.
|
|
855
|
+
*/
|
|
856
|
+
readonly output: AgentTurn | null;
|
|
857
|
+
/**
|
|
858
|
+
* The system prompt, durable beside the transcript it governs. `null` when
|
|
859
|
+
* the config named none. Read by `brainCall` on every model call — a
|
|
860
|
+
* rehydrated or replayed run prompts with what the Model says, never with
|
|
861
|
+
* what a closure happens to hold now.
|
|
862
|
+
*/
|
|
863
|
+
readonly instructions: string | null;
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* The unified terminal failure the agent settles on. The agent terminates `failed`
|
|
867
|
+
* through TWO independent slice channels:
|
|
868
|
+
*
|
|
869
|
+
* - `state.failure` (`AgentFailure`) — the AGENT'S OWN annotation
|
|
870
|
+
* (`turn_limit` livelock guard / `elapsed_limit` wall-clock guard / `llm`
|
|
871
|
+
* exhausted-retry). Set WITHOUT moving
|
|
872
|
+
* `run.phase` (it stays `running`); `isSettled` treats a non-null `failure`
|
|
873
|
+
* as terminal regardless of `run.phase`.
|
|
874
|
+
* - `state.run.failure` (`RunFailure`) — the monitored-run channel
|
|
875
|
+
* (`deadline` watchdog / `stage` failure), present iff `run.phase` is
|
|
876
|
+
* `"failed"`.
|
|
877
|
+
*
|
|
878
|
+
* Callers used to union these by hand at every status question. `status`
|
|
879
|
+
* collapses them into ONE `failure`, so a consumer reads `status(s).failure`
|
|
880
|
+
* without knowing which channel produced it.
|
|
881
|
+
*/
|
|
882
|
+
type AgentTerminalFailure<Stage> = AgentFailure | RunFailure<Stage>;
|
|
883
|
+
/**
|
|
884
|
+
* The agent's lifecycle status — THE single typed channel for "what is this run
|
|
885
|
+
* doing?". A discriminated union on `kind` so any change to the
|
|
886
|
+
* private slice shape (`Awaiting`, the failure channels, `run.phase`) forces a
|
|
887
|
+
* compile error at the call sites that switch on it, instead of silently
|
|
888
|
+
* breaking a hand-rolled re-derivation. Make-invalid-states-unrepresentable:
|
|
889
|
+
*
|
|
890
|
+
* - `idle` — never started: `run.phase === "idle"`, the slice straight
|
|
891
|
+
* out of `init` before any `agent_start`. Distinct from
|
|
892
|
+
* `running` so a "start or resume?" decision never boots a
|
|
893
|
+
* run that has not begun (#92); the next move is `agent_start`.
|
|
894
|
+
* - `running` — live, NOT awaiting tools (a brain call is in flight, or the
|
|
895
|
+
* run is between stages). Not resumable on cold wake by
|
|
896
|
+
* itself — the in-flight brain call re-fires from `boot`.
|
|
897
|
+
* A `stale` run reads here too: monitored-run documents it
|
|
898
|
+
* as a soft, still-resumable live mark that the next
|
|
899
|
+
* `progress` flips back to `running`.
|
|
900
|
+
* - `suspended` — the RESUMABLE case: running + a live conversation +
|
|
901
|
+
* `awaiting.kind === "tools"`. `pending` is the outstanding
|
|
902
|
+
* tool calls (`conversation.awaiting` … the in-flight batch),
|
|
903
|
+
* read off `tools.running`. This is exactly the shape
|
|
904
|
+
* `agentIsResumable` re-derived by hand.
|
|
905
|
+
* - `done` — the pipeline finished; `output` is the terminal model turn
|
|
906
|
+
* stamped on `state.output` (issue #46), `null` if the run
|
|
907
|
+
* produced no turn.
|
|
908
|
+
* - `failed` — terminal failure; `failure` is the UNIFIED channel
|
|
909
|
+
* (`AgentTerminalFailure`), absorbing the `state.failure` vs
|
|
910
|
+
* `state.run.failure` dual channel so callers stop unioning.
|
|
911
|
+
* - `cancelled` — stopped because the caller asked — an aborted `AbortSignal`,
|
|
912
|
+
* or their own `stopWhen` answering `true` at a turn boundary;
|
|
913
|
+
* `at` is when the stop landed. Terminal and distinct from
|
|
914
|
+
* `failed`: nothing went wrong, the caller asked, so a consumer
|
|
915
|
+
* branching on failure does not have to except one reason. There is no
|
|
916
|
+
* `output` — a cancelled run produced no terminal turn, and a
|
|
917
|
+
* `null` one here would be indistinguishable from a `done` run
|
|
918
|
+
* that produced none.
|
|
919
|
+
*/
|
|
920
|
+
type AgentStatus<Stage> = {
|
|
921
|
+
readonly kind: "idle";
|
|
922
|
+
} | {
|
|
923
|
+
readonly kind: "running";
|
|
924
|
+
} | {
|
|
925
|
+
readonly kind: "suspended";
|
|
926
|
+
readonly pending: readonly ToolCall[];
|
|
927
|
+
} | {
|
|
928
|
+
readonly kind: "done";
|
|
929
|
+
readonly output: AgentTurn | null;
|
|
930
|
+
} | {
|
|
931
|
+
readonly kind: "failed";
|
|
932
|
+
readonly failure: AgentTerminalFailure<Stage>;
|
|
933
|
+
} | {
|
|
934
|
+
readonly kind: "cancelled";
|
|
935
|
+
readonly at: number;
|
|
936
|
+
};
|
|
937
|
+
/**
|
|
938
|
+
* Ask where an agent run stands: pass its state, get back one of `idle`,
|
|
939
|
+
* `running`, `suspended` (with the tool calls it is waiting on), `done` (with
|
|
940
|
+
* the output) or `failed` (with the failure).
|
|
941
|
+
*
|
|
942
|
+
* Read this instead of picking at the state's own fields. PURE — reads no
|
|
943
|
+
* clock or randomness, allocates one small record.
|
|
944
|
+
*
|
|
945
|
+
* Ordering is terminal-first so a settled run never reports `running`, and a
|
|
946
|
+
* never-started one never reports live:
|
|
947
|
+
*
|
|
948
|
+
* 1. `state.failure` (the agent's own annotation, set WITHOUT moving
|
|
949
|
+
* `run.phase`) → `failed`. Checked first because `turn_limit` / `llm`
|
|
950
|
+
* leave `run.phase === "running"`; reading `run.phase` first would
|
|
951
|
+
* misreport such a run as `running`. This is the canonical failure source
|
|
952
|
+
* when present.
|
|
953
|
+
* 2. `run.phase === "idle"` → `idle`. Never started (#92); after the failure
|
|
954
|
+
* check so the annotation stays canonical, before every live reading so
|
|
955
|
+
* an `init` slice is never mistaken for a run in flight.
|
|
956
|
+
* 3. `run.phase === "failed"` → `failed`, carrying `run.failure` (deadline /
|
|
957
|
+
* stage). The fallback channel.
|
|
958
|
+
* 4. `run.phase === "done"` → `done` with `state.output` (#46).
|
|
959
|
+
* 5. `run.phase === "cancelled"` → `cancelled`, carrying when the stop landed.
|
|
960
|
+
* 6. `running` / `stale` + conversation + `awaiting.kind === "tools"` →
|
|
961
|
+
* `suspended` with the outstanding tool calls (`tools.running`). THE
|
|
962
|
+
* resumability condition; otherwise the run reads `running`.
|
|
963
|
+
*
|
|
964
|
+
* Steps 2–6 are ONE exhaustive `switch` over `run.phase` closed by `absurd`, not
|
|
965
|
+
* an `if` chain with a trailing `return { kind: "running" }`. Under the chain a
|
|
966
|
+
* phase added to the slice and forgotten here reported as `running` — a terminal
|
|
967
|
+
* run described as live, which is the worst answer this function can give and one
|
|
968
|
+
* nothing would have caught. The switch turns that omission into a type error.
|
|
969
|
+
*/
|
|
970
|
+
declare function status<Stage, P extends string, O extends Record<P, unknown>, R>(s: AgentState<Stage, P, O, R>): AgentStatus<Stage>;
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* @demlik/tea/agent — the context-compaction seam.
|
|
974
|
+
*
|
|
975
|
+
* The opt-in transcript snapshot-and-compact policy, shaped exactly like
|
|
976
|
+
* `AgentSnapshotConfig` (see `./types`) so a short agent pays nothing and cannot
|
|
977
|
+
* even mention the machinery. This module owns the whole `$compact` vocabulary:
|
|
978
|
+
* the reserved purpose, the summary schema, the consumer policy, the config
|
|
979
|
+
* discriminant, and the dedicated `compact_run` / `compact_ok` / `compact_err`
|
|
980
|
+
* Cmd + Msgs re-keyed off the composed resilient call at the agent boundary.
|
|
981
|
+
*/
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* The reserved purpose the compaction round-trip runs under. Compaction
|
|
985
|
+
* is a DEDICATED resilient LLM call (design B1), NOT one of the consumer's brain
|
|
986
|
+
* purposes `P` — so it carries its own purpose literal, kept in the agent's
|
|
987
|
+
* private `$`-namespace (the same reserved-namespace discipline `with-resilience`
|
|
988
|
+
* uses) so it never collides with a consumer purpose. A consumer never names it.
|
|
989
|
+
*/
|
|
990
|
+
type CompactionPurpose = "$compact";
|
|
991
|
+
/** The reserved compaction purpose's value — the single in-flight summarize call's key. */
|
|
992
|
+
declare const COMPACTION_PURPOSE: CompactionPurpose;
|
|
993
|
+
/**
|
|
994
|
+
* The purpose→output map for the compaction LLM call — the single reserved
|
|
995
|
+
* `$compact` purpose mapping to a {@link CompactionSummary}. Mirrors the brain
|
|
996
|
+
* call's `O` map, scoped to the one compaction purpose, so the composed
|
|
997
|
+
* `createLlmCall` parses the summarize output under the same structured-output
|
|
998
|
+
* contract the brain call uses.
|
|
999
|
+
*/
|
|
1000
|
+
interface CompactionOutputs extends Record<CompactionPurpose, unknown> {
|
|
1001
|
+
readonly $compact: CompactionSummary;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Narrow an unknown to a {@link CompactionSummary} — the runtime witness for the
|
|
1005
|
+
* compaction call's structured output. Checks the one load-bearing field
|
|
1006
|
+
* (`summary` is a string). PURE — allocates no Error.
|
|
1007
|
+
*/
|
|
1008
|
+
declare function isCompactionSummary(value: unknown): value is CompactionSummary;
|
|
1009
|
+
/**
|
|
1010
|
+
* The `Schema<CompactionSummary>` the compaction call binds — tea's own parse
|
|
1011
|
+
* target for the summarize round-trip (it OWNS the `$compact` purpose's output).
|
|
1012
|
+
* Throws on a non-summary (the zod-style `parse` contract the llm-call handler
|
|
1013
|
+
* relies on), so a malformed summary is a `compact_err` (errors are data), never
|
|
1014
|
+
* a corrupt fold-back.
|
|
1015
|
+
*/
|
|
1016
|
+
declare const compactionSummarySchema: Schema<CompactionSummary>;
|
|
1017
|
+
/**
|
|
1018
|
+
* The result a compaction round-trip produces — the model's summary of the
|
|
1019
|
+
* folded-away turns. Tea OWNS this shape (the `$compact` purpose's output): the
|
|
1020
|
+
* fold-back (`compact_ok`) writes `summary` into a synthetic head `AgentTurn`
|
|
1021
|
+
* (design A1), so a consumer's `loadMessages` reads it as a normal turn with no
|
|
1022
|
+
* watermark to learn. Generic-free — the summary is always plain text.
|
|
1023
|
+
*/
|
|
1024
|
+
interface CompactionSummary {
|
|
1025
|
+
/** The model's summary of the folded-away (oldest) turns + their tool records. */
|
|
1026
|
+
readonly summary: string;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* The consumer's compaction policy. One PURE trigger; the impure
|
|
1030
|
+
* summarize round-trip is composed by the agent (a dedicated resilient LLM call
|
|
1031
|
+
* under the reserved `$compact` purpose, inheriting retry/backoff — design B1),
|
|
1032
|
+
* so the policy never reaches the model itself.
|
|
1033
|
+
*
|
|
1034
|
+
* - `planCompaction` — PURE: given the live conversation ABOUT to fire a brain
|
|
1035
|
+
* call, return how many of the OLDEST turns to fold into a summary, or `0`
|
|
1036
|
+
* to skip. No clock, no RNG → replay re-decides identically (design D). A
|
|
1037
|
+
* char-length / turn-count / real deterministic-tokenizer heuristic all
|
|
1038
|
+
* qualify. Returning `>= turns.length` is clamped to "fold all but keep the
|
|
1039
|
+
* loop alive" by the trigger (it never folds the turn currently in flight).
|
|
1040
|
+
* - `payloadOf` — build the per-call prompt payload the compaction
|
|
1041
|
+
* `MessageLoader` reads (the turns being folded). Omit → `null` (the loader,
|
|
1042
|
+
* if any, reads the conversation off the agent's own state). Opaque to the
|
|
1043
|
+
* agent, exactly like the brain call's `payloadOf`.
|
|
1044
|
+
*/
|
|
1045
|
+
interface CompactionPolicy<R, Msg = unknown> {
|
|
1046
|
+
/** PURE trigger: how many OLDEST turns to fold (`0` = skip). No clock/RNG. */
|
|
1047
|
+
readonly planCompaction: (conversation: Conversation<R>) => number;
|
|
1048
|
+
/** Build the summarize call's prompt payload from the turns being folded. Omit → `null`. */
|
|
1049
|
+
readonly payloadOf?: (conversation: Conversation<R>, folding: number) => unknown;
|
|
1050
|
+
/** DI port — the SDK / message loader for the summarize call. Omit → invoke with `[]`. */
|
|
1051
|
+
readonly loadMessages?: MessageLoader<CompactionPurpose, Msg>;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* The compaction discriminant, shaped exactly like {@link
|
|
1055
|
+
* AgentSnapshotConfig}. Compaction is either OFF —
|
|
1056
|
+
* `compaction` is structurally absent and the agent never emits a `compact_run`
|
|
1057
|
+
* Cmd — or ON, in which case `compaction` is a {@link CompactionPolicy}. A `{
|
|
1058
|
+
* compaction?: never }` member (rather than a bare optional) makes the OFF case
|
|
1059
|
+
* load-bearing: it forbids passing `compaction` at all, so `toMachine`
|
|
1060
|
+
* config-derives whether the `compact_run` interpret handler is REQUIRED (ON) or
|
|
1061
|
+
* FORBIDDEN (OFF) — never a silent no-op handler, the same type-lie-killer
|
|
1062
|
+
* `AgentSnapshotConfig` uses.
|
|
1063
|
+
*/
|
|
1064
|
+
type AgentCompactionConfig<R, Msg> = {
|
|
1065
|
+
readonly compaction?: never;
|
|
1066
|
+
} | {
|
|
1067
|
+
readonly compaction: CompactionPolicy<R, Msg>;
|
|
1068
|
+
};
|
|
1069
|
+
/**
|
|
1070
|
+
* The "summarize the oldest N turns" effect Cmd — the compaction round-trip's
|
|
1071
|
+
* carrier. It mirrors the brain `resilient_run` Cmd's shape (a `key` + the
|
|
1072
|
+
* plain `LlmCall<CompactionPurpose>` input, no closures) but under the dedicated
|
|
1073
|
+
* `compact_run` discriminant so it routes to the compaction interpret handler, not
|
|
1074
|
+
* the brain one. The handler composes the resilient-call wrapper (retry/backoff) and
|
|
1075
|
+
* returns the re-keyed `compact_ok` / `compact_err` settle for re-entry.
|
|
1076
|
+
*/
|
|
1077
|
+
type AgentCompactRunCmd = Cmd<typeof MsgType.CompactRun> & {
|
|
1078
|
+
readonly key: CompactionPurpose;
|
|
1079
|
+
readonly input: LlmCall<CompactionPurpose>;
|
|
1080
|
+
};
|
|
1081
|
+
/** The compaction round-trip success settle Msg — carries the parsed {@link CompactionSummary}. */
|
|
1082
|
+
type AgentCompactOkMsg = {
|
|
1083
|
+
readonly type: typeof MsgType.CompactOk;
|
|
1084
|
+
readonly key: CompactionPurpose;
|
|
1085
|
+
readonly result: LlmOk<CompactionPurpose, CompactionOutputs>;
|
|
1086
|
+
readonly at: number;
|
|
1087
|
+
};
|
|
1088
|
+
/** The compaction round-trip failure settle Msg — carries the typed {@link LlmErr}. */
|
|
1089
|
+
type AgentCompactErrMsg = {
|
|
1090
|
+
readonly type: typeof MsgType.CompactErr;
|
|
1091
|
+
readonly key: CompactionPurpose;
|
|
1092
|
+
readonly error: LlmErr<CompactionPurpose>;
|
|
1093
|
+
readonly at: number;
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* The CONFIG-DERIVED compaction obligation on `toMachine`'s `toolInterpret`
|
|
1097
|
+
* — the exact twin of {@link SnapshotInterpret}.
|
|
1098
|
+
* Resolves on the `Compact` discriminant the compaction overload of
|
|
1099
|
+
* `createAgent` fixes:
|
|
1100
|
+
*
|
|
1101
|
+
* - compaction ON → a REQUIRED `compact_run` handler
|
|
1102
|
+
* (`Interpret<M, AgentCompactRunCmd, Ctx>`). The summarize round-trip MUST be
|
|
1103
|
+
* wired — the agent never defaults it to a no-op.
|
|
1104
|
+
* - compaction OFF → `{ compact_run?: never }`. The handler is FORBIDDEN: with no
|
|
1105
|
+
* policy the agent never emits a `compact_run` Cmd, so wiring it would be
|
|
1106
|
+
* dead code. The consumer cannot even mention `compact_run`.
|
|
1107
|
+
*
|
|
1108
|
+
* Same type-lie-killer as the snapshot derivation: the obligation is present
|
|
1109
|
+
* EXACTLY when the Cmd can fire, never as a silent gap the agent backfills.
|
|
1110
|
+
*/
|
|
1111
|
+
type CompactInterpret<M extends {
|
|
1112
|
+
type: string;
|
|
1113
|
+
}, Compact extends boolean, Ctx> = Compact extends true ? Interpret<M, AgentCompactRunCmd, Ctx> : {
|
|
1114
|
+
readonly compact_run?: never;
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* @demlik/tea/agent — `tool()` + `toolRouter()`: declare a tool once and derive
|
|
1119
|
+
* what a consumer used to hand-write twice — the `toolOf` mapping from a
|
|
1120
|
+
* model's `ToolCall` to an effect Cmd, and the interpret handler that runs it
|
|
1121
|
+
* and settles it.
|
|
1122
|
+
*
|
|
1123
|
+
* A tool is a `Cmd.define`d effect whose input is the model's call —
|
|
1124
|
+
* `{ callId, args }` — with the handler colocated on the constructor. The
|
|
1125
|
+
* router folds a set of them into one `toolOf` and one interpret table; the
|
|
1126
|
+
* handlers settle through the minted `<name>_ok` / `<name>_err` Msgs, errors as
|
|
1127
|
+
* `{ _tag }` data (ADR 0011). `createAgent(...).toMachine({ tools })` folds
|
|
1128
|
+
* those settles into the conversation, so a consumer names nothing twice.
|
|
1129
|
+
*/
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* The prefix a protocol discriminant was minted from: `resilient_ok` →
|
|
1133
|
+
* `resilient`, `compact_run` → `compact`. A tool named by that prefix would
|
|
1134
|
+
* mint the same `<name>_ok` / `<name>_err` the agent's reducer already owns.
|
|
1135
|
+
*/
|
|
1136
|
+
type SettlePrefixOf<T extends string> = T extends `${infer P}_ok` | `${infer P}_err` | `${infer P}_run` ? P : never;
|
|
1137
|
+
declare const REJECTED_TYPE = "tool_rejected";
|
|
1138
|
+
declare const SNAPSHOT_WRITE_TYPE = "snapshot_write";
|
|
1139
|
+
/**
|
|
1140
|
+
* A tool name `tool()` refuses. A tool's name is its Cmd `type` — so
|
|
1141
|
+
* its interpret key — and the prefix of its settle Msgs, so every string
|
|
1142
|
+
* already spoken by the protocol is taken twice over: the settle prefixes
|
|
1143
|
+
* (`resilient`, `agent_tool`, `compact`) would overwrite the agent's own
|
|
1144
|
+
* reducer handlers through `toMachine`'s last-wins spread, and the discriminants
|
|
1145
|
+
* themselves (`compact_run`, `resilient_run`, …) would shadow the interpret
|
|
1146
|
+
* handler of that name. Both halves derive from `MsgType`: a new entry there
|
|
1147
|
+
* reserves its name and its prefix with no second edit. The two names outside
|
|
1148
|
+
* the protocol are the router's own `tool_rejected` and the checkpoint handler
|
|
1149
|
+
* `snapshot_write`, which `toMachine` merges under the consumer's key.
|
|
1150
|
+
*/
|
|
1151
|
+
type ReservedToolName = MsgTypeValue | SettlePrefixOf<MsgTypeValue> | typeof REJECTED_TYPE | typeof SNAPSHOT_WRITE_TYPE;
|
|
1152
|
+
/**
|
|
1153
|
+
* Widens to `unknown` for a name outside the reserved set and to `never`
|
|
1154
|
+
* inside it, so `Name & NotReserved<Name>` is the name itself or
|
|
1155
|
+
* unconstructible. A widened `string` passes here — the runtime check in
|
|
1156
|
+
* `tool()` is what catches a reserved name built from one.
|
|
1157
|
+
*/
|
|
1158
|
+
type NotReserved<Name extends string> = Name extends ReservedToolName ? never : unknown;
|
|
1159
|
+
/** Whether `name` is one `tool()` refuses — the set `ReservedToolName` types. */
|
|
1160
|
+
declare function isReservedToolName(name: string): name is ReservedToolName;
|
|
1161
|
+
/**
|
|
1162
|
+
* The input a tool Cmd carries: the model's `callId` (the fan-out identity the
|
|
1163
|
+
* settle folds back on) and the `args` already parsed against the tool's
|
|
1164
|
+
* `input` schema — the boundary parses, the handler trusts.
|
|
1165
|
+
*/
|
|
1166
|
+
type ToolInput<Args> = {
|
|
1167
|
+
readonly callId: string;
|
|
1168
|
+
readonly args: Args;
|
|
1169
|
+
};
|
|
1170
|
+
/**
|
|
1171
|
+
* The router-minted failure beside a tool's declared tags: the handler threw
|
|
1172
|
+
* (or rejected) with something that is not a declared `{ _tag }`. Plain data
|
|
1173
|
+
* the model sees as a reason, never a rejection out of the interpret handler.
|
|
1174
|
+
*/
|
|
1175
|
+
type ToolThrown = {
|
|
1176
|
+
readonly _tag: "thrown";
|
|
1177
|
+
readonly message: string;
|
|
1178
|
+
};
|
|
1179
|
+
/**
|
|
1180
|
+
* The typed success constructor a handler receives: `ok(value)` with `Ok`
|
|
1181
|
+
* fixed to what the `ok` schema parses, so a value of the wrong shape is
|
|
1182
|
+
* refused where it is written.
|
|
1183
|
+
*/
|
|
1184
|
+
type ToolOk<Ok, E extends Tagged> = (value: Ok) => Result<Ok, E>;
|
|
1185
|
+
/**
|
|
1186
|
+
* The typed failure constructor a handler receives: `fail({ _tag })` with `E`
|
|
1187
|
+
* fixed to the declared tags, so the literal is checked against them where it
|
|
1188
|
+
* is written. (A bare `{ _tag: "x" }` built elsewhere infers `_tag: string`
|
|
1189
|
+
* and cannot be — the parameter type is what keeps it literal.)
|
|
1190
|
+
*/
|
|
1191
|
+
type ToolFail<Ok, E extends Tagged> = (error: E) => Result<Ok, E>;
|
|
1192
|
+
/**
|
|
1193
|
+
* The two constructors a handler is handed, one per channel — `ok` for the
|
|
1194
|
+
* value the `ok` schema parses, `fail` for a declared `{ _tag }`. Both are
|
|
1195
|
+
* tea's, so a handler settles either arm without naming the result library
|
|
1196
|
+
* underneath.
|
|
1197
|
+
*/
|
|
1198
|
+
type ToolConstructors<Ok, E extends Tagged> = {
|
|
1199
|
+
readonly ok: ToolOk<Ok, E>;
|
|
1200
|
+
readonly fail: ToolFail<Ok, E>;
|
|
1201
|
+
};
|
|
1202
|
+
/**
|
|
1203
|
+
* A tool's handler: the parsed `args`, the ctx slice `requirements` named, and the
|
|
1204
|
+
* typed `{ ok, fail }`, to a result over the declared channels — `Ok` is what
|
|
1205
|
+
* the `ok` schema parses, `E` the declared `_tag` union. An undeclared tag
|
|
1206
|
+
* does not compile.
|
|
1207
|
+
*/
|
|
1208
|
+
type ToolHandler<Args, Ok, E extends Tagged, R> = (args: Args, ctx: R & PortEmitter, settle: ToolConstructors<Ok, E>) => Promise<Result<Ok, E>>;
|
|
1209
|
+
/**
|
|
1210
|
+
* What `tool()` returns: the `Cmd.define`d constructor (so `Settled<typeof t>`
|
|
1211
|
+
* / `CmdOf<typeof t>` read it like any def) plus the colocated `interpret` handler,
|
|
1212
|
+
* the bare `args` schema the router parses a call against, and the
|
|
1213
|
+
* `description` a provider adapter declares to the model beside that schema.
|
|
1214
|
+
* `E` is the full failure union the Cmd settles with — the declared tags plus
|
|
1215
|
+
* `thrown`.
|
|
1216
|
+
*/
|
|
1217
|
+
type ToolDef<Name extends string, Args, Ok, E extends Tagged, R> = CmdDef<Name, ToolInput<Args>, Ok, E, R> & {
|
|
1218
|
+
readonly description: string;
|
|
1219
|
+
readonly args: z.ZodType<Args>;
|
|
1220
|
+
/**
|
|
1221
|
+
* The timeout / retry knob this tool declared, or `null` when it declared
|
|
1222
|
+
* neither. `toolRouter` serves it to the agent as `resilienceOf`.
|
|
1223
|
+
*/
|
|
1224
|
+
readonly resilience: ToolResilience | null;
|
|
1225
|
+
readonly interpret: (cmd: CmdValue<Name, ToolInput<Args>, E, R>, ctx: R & PortEmitter) => Promise<Settled<CmdDef<Name, ToolInput<Args>, Ok, E, R>>>;
|
|
1226
|
+
};
|
|
1227
|
+
/** The declaration-erased view the router reads. */
|
|
1228
|
+
type AnyToolDef = AnyCmdDef & {
|
|
1229
|
+
readonly description: string;
|
|
1230
|
+
readonly args: z.ZodType;
|
|
1231
|
+
readonly resilience: ToolResilience | null;
|
|
1232
|
+
readonly interpret: (cmd: never, ctx: never) => Promise<unknown>;
|
|
1233
|
+
};
|
|
1234
|
+
/**
|
|
1235
|
+
* Declare one tool the model may call — its name, the schemas for its arguments
|
|
1236
|
+
* and result, the failures it may return and the handler that runs it — and get
|
|
1237
|
+
* back a `Cmd<T, E, R>` definition, whose `T` is what `ok` parses and whose `E`
|
|
1238
|
+
* is the `err` tag union, that you pass to `toolRouter` or `defineAgent`.
|
|
1239
|
+
*
|
|
1240
|
+
* So a tool's two declared channels ARE the kernel's two typed effect channels
|
|
1241
|
+
* (ADR 0014), spelled in schemas rather than in type parameters: a settled
|
|
1242
|
+
* failure keeps its `_tag` into the conversation, so a reducer switching over
|
|
1243
|
+
* `err`'s tags is exhaustive and an unhandled one is a compile error.
|
|
1244
|
+
*
|
|
1245
|
+
* `name` is the name the model calls it
|
|
1246
|
+
* by; `description` is the model-facing sentence — the one place it lives — a
|
|
1247
|
+
* provider adapter declares to the model beside the schema (Anthropic
|
|
1248
|
+
* `description`, OpenAI `function.description`) so the model can tell when to
|
|
1249
|
+
* call the tool, read off `def.description`, never off the `input` schema;
|
|
1250
|
+
* `input` parses the model's `args`; `ok` parses the handler's value at the
|
|
1251
|
+
* edge; `err` is the `_tag` list the handler may fail with; `requirements` is the ctx
|
|
1252
|
+
* slice it reads, demanded at `run`. The handler returns one of the two
|
|
1253
|
+
* constructors it is handed — `ok(value)` or the typed `fail({ _tag })`; a
|
|
1254
|
+
* throw settles `<name>_err` — with the thrown `_tag` when it is a declared
|
|
1255
|
+
* one, else as `{ _tag: "thrown", message }`.
|
|
1256
|
+
*
|
|
1257
|
+
* A `ReservedToolName` does not compile as `name`, and one that reaches here
|
|
1258
|
+
* as a widened `string` throws — the same declaration-bug refusal
|
|
1259
|
+
* `toolRouter` gives a name declared twice.
|
|
1260
|
+
*/
|
|
1261
|
+
declare function tool<const Name extends string, Args, Ok, const Tags extends readonly string[], R = unknown>(name: Name & NotReserved<Name>, spec: {
|
|
1262
|
+
readonly description: string;
|
|
1263
|
+
readonly input: z.ZodType<Args>;
|
|
1264
|
+
readonly ok: z.ZodType<Ok>;
|
|
1265
|
+
readonly err: Tags;
|
|
1266
|
+
readonly requirements?: Requirements<R>;
|
|
1267
|
+
/**
|
|
1268
|
+
* The budget one call of this tool gets, in ms — the overall cap, measured
|
|
1269
|
+
* from the first attempt and not restarted by a retry. When it elapses the
|
|
1270
|
+
* call settles as a `ToolOutcome` error carrying `_tag: "timeout"` beside
|
|
1271
|
+
* its `reason`, and the loop moves on; the attempt is NOT cancelled (a promise cannot be), so it runs to its
|
|
1272
|
+
* own end and its late settle folds nothing. Omit → no cap.
|
|
1273
|
+
*/
|
|
1274
|
+
readonly timeoutMs?: number;
|
|
1275
|
+
/**
|
|
1276
|
+
* The backoff ladder a failed attempt of this tool climbs — the same
|
|
1277
|
+
* `{ baseMs, factor, capMs, jitter, maxAttempts }` shape the brain call's
|
|
1278
|
+
* `retry` takes. The ladder is folded into the Model and its wait is a timer
|
|
1279
|
+
* Sub, so a process killed between two attempts resumes at the attempt it
|
|
1280
|
+
* was on. A spent budget settles as a `ToolOutcome` error carrying
|
|
1281
|
+
* `_tag: "retry_exhausted"` beside its `reason`, with the attempt count and
|
|
1282
|
+
* the last attempt's reason as the `attempts` / `last` fields.
|
|
1283
|
+
* Omit → the first failure is the outcome.
|
|
1284
|
+
*/
|
|
1285
|
+
readonly retry?: ToolResilience["retry"];
|
|
1286
|
+
}, handler: ToolHandler<Args, Ok, TaggedError<Tags[number]>, R>): ToolDef<Name, Args, Ok, TaggedError<Tags[number] | "thrown">, R>;
|
|
1287
|
+
/**
|
|
1288
|
+
* A call the router could not hand to a tool: the model named a tool nobody
|
|
1289
|
+
* declared, or its `args` failed the tool's `input` schema. `toolOf` is total —
|
|
1290
|
+
* it never throws inside the reducer — so the refusal rides its own Cmd and
|
|
1291
|
+
* settles `tool_rejected_err`, which the loop folds like any tool failure.
|
|
1292
|
+
*/
|
|
1293
|
+
type ToolRejection = {
|
|
1294
|
+
readonly _tag: "unknown_tool";
|
|
1295
|
+
readonly name: string;
|
|
1296
|
+
} | {
|
|
1297
|
+
readonly _tag: "malformed_args";
|
|
1298
|
+
readonly name: string;
|
|
1299
|
+
readonly issues: ReadonlyArray<{
|
|
1300
|
+
readonly path: string;
|
|
1301
|
+
readonly message: string;
|
|
1302
|
+
}>;
|
|
1303
|
+
};
|
|
1304
|
+
declare const rejected: CmdDef<"tool_rejected", {
|
|
1305
|
+
readonly callId: string;
|
|
1306
|
+
readonly error: ToolRejection;
|
|
1307
|
+
}, never, {
|
|
1308
|
+
readonly [detail: string]: unknown;
|
|
1309
|
+
readonly _tag: "unknown_tool";
|
|
1310
|
+
} | {
|
|
1311
|
+
readonly [detail: string]: unknown;
|
|
1312
|
+
readonly _tag: "malformed_args";
|
|
1313
|
+
}, unknown>;
|
|
1314
|
+
/** The Cmd `tool_rejected` builds — the router-owned variant of `ToolCmd`. */
|
|
1315
|
+
type ToolRejectedCmd = CmdOf<typeof rejected>;
|
|
1316
|
+
/** The Cmd union a router's `toolOf` produces — `TC` for `createAgent`. */
|
|
1317
|
+
type ToolCmd<T extends AnyToolDef> = CmdOf<T> | ToolRejectedCmd;
|
|
1318
|
+
/** The settled Msg union a router's handlers return — folded by `toMachine`. */
|
|
1319
|
+
type ToolMsg<T extends AnyToolDef> = Settled<T> | Settled<typeof rejected>;
|
|
1320
|
+
/**
|
|
1321
|
+
* `ToolCmd<T>` as `toMachine` reads it: `never` for `T = never` — the "no
|
|
1322
|
+
* router" reading it defaults to — so the router-owned `tool_rejected` arm
|
|
1323
|
+
* does not leak into a machine that wired no router.
|
|
1324
|
+
*/
|
|
1325
|
+
type WiredToolCmd<T extends AnyToolDef> = [T] extends [never] ? never : ToolCmd<T>;
|
|
1326
|
+
/** `ToolMsg<T>` as `toMachine` / `agentEvents` read it — see `WiredToolCmd`. */
|
|
1327
|
+
type WiredToolMsg<T extends AnyToolDef> = [T] extends [never] ? never : ToolMsg<T>;
|
|
1328
|
+
/** The union of every tool's `ok` value — `R` for `createAgent`. */
|
|
1329
|
+
type ToolResult<T extends AnyToolDef> = OkOf<T>;
|
|
1330
|
+
/**
|
|
1331
|
+
* Every failure a router over `T` can settle with — the union `outcomeOf`'s
|
|
1332
|
+
* error arm is typed from. Five sources, and a consumer branching on `_tag`
|
|
1333
|
+
* meets all five: each tool's DECLARED tags plus the `thrown` `tool()` appends
|
|
1334
|
+
* (`ErrOf<T>`), the kernel's `malformed_result` (a handler returned a value its
|
|
1335
|
+
* own `ok` schema rejects), the router's own `unknown_tool` / `malformed_args`
|
|
1336
|
+
* rejections, and the ladder's `timeout` / `retry_exhausted` (#117) — which end
|
|
1337
|
+
* a call from outside the handler and so are nothing a tool can declare.
|
|
1338
|
+
*/
|
|
1339
|
+
type ToolError<T extends AnyToolDef> = ErrOf<T> | MalformedResult | ToolRejection | ToolResilienceError;
|
|
1340
|
+
/**
|
|
1341
|
+
* The error outcome a router over `T` produces: `{ kind: "error", _tag,
|
|
1342
|
+
* …payload, reason }`, discriminable on `_tag` over {@link ToolError}. This is
|
|
1343
|
+
* what `defineAgent`'s `onToolError` hands the consumer.
|
|
1344
|
+
*/
|
|
1345
|
+
type ToolFailureOf<T extends AnyToolDef> = TaggedFailure<ToolError<T>>;
|
|
1346
|
+
/** One settled tool, read back off a `ToolMsg` by `outcomeOf`. */
|
|
1347
|
+
type ToolSettlement<R, E extends Tagged = Tagged> = {
|
|
1348
|
+
readonly callId: string;
|
|
1349
|
+
readonly outcome: {
|
|
1350
|
+
readonly kind: "ok";
|
|
1351
|
+
readonly result: R;
|
|
1352
|
+
} | TaggedFailure<E>;
|
|
1353
|
+
};
|
|
1354
|
+
/**
|
|
1355
|
+
* What `toolRouter()` returns: the derived `toolOf` for `createAgent`'s config,
|
|
1356
|
+
* the interpret table `toMachine({ tools })` merges, the defs it puts on
|
|
1357
|
+
* `Machine.cmds`, and the one reader that turns a settled Msg back into the
|
|
1358
|
+
* conversation's `ToolOutcome`.
|
|
1359
|
+
*/
|
|
1360
|
+
interface ToolRouter<T extends AnyToolDef> {
|
|
1361
|
+
/** Every def the router settles through — hand to `Machine.cmds`. */
|
|
1362
|
+
readonly defs: readonly AnyCmdDef[];
|
|
1363
|
+
/** The `toolOf` for `createAgent`: total, pure, parses `args` at the edge. */
|
|
1364
|
+
readonly toolOf: (call: ToolCall) => ToolCmd<T>;
|
|
1365
|
+
/** One interpret handler per tool plus the `tool_rejected` handler. */
|
|
1366
|
+
readonly interpret: Interpret<ToolMsg<T>, ToolCmd<T>, NoCtx>;
|
|
1367
|
+
/**
|
|
1368
|
+
* The timeout / retry knob the called tool declared — `AgentConfigCore`'s
|
|
1369
|
+
* `toolResilienceOf` seam, filled from the `tool()` specs. `null` for a tool
|
|
1370
|
+
* that declared neither field and for a call no tool answers (the rejection
|
|
1371
|
+
* path settles in the reducer and never runs an effect to time out). PURE.
|
|
1372
|
+
*/
|
|
1373
|
+
readonly resilienceOf: (call: ToolCall) => ToolResilience | null;
|
|
1374
|
+
/**
|
|
1375
|
+
* Read a settled tool off a Msg: `null` when the Msg is not one of this
|
|
1376
|
+
* router's `<name>_ok` / `<name>_err`. The one place a `{ _tag }` failure is
|
|
1377
|
+
* rendered to the `reason` string the conversation carries — and the tag and
|
|
1378
|
+
* its payload ride beside that rendering, never instead of it (#115).
|
|
1379
|
+
*/
|
|
1380
|
+
readonly outcomeOf: (msg: {
|
|
1381
|
+
readonly type: string;
|
|
1382
|
+
}) => ToolSettlement<ToolResult<T>, ToolError<T>> | null;
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Fold a set of `tool()`s into one router — pass it the tools, get back the
|
|
1386
|
+
* lookup `createAgent` needs, the handlers `toMachine` merges, and a reader that
|
|
1387
|
+
* turns a settled message back into a plain outcome.
|
|
1388
|
+
*
|
|
1389
|
+
* Two tools with one name is a declaration bug, refused here rather than by a
|
|
1390
|
+
* silent last-wins map.
|
|
1391
|
+
*/
|
|
1392
|
+
declare function toolRouter<T extends AnyToolDef>(tools: readonly T[]): ToolRouter<T>;
|
|
1393
|
+
/**
|
|
1394
|
+
* Give a router's interpret cells real wall-clock overlap without touching the
|
|
1395
|
+
* kernel — pass the table `toolRouter` built, get back one whose cells launch
|
|
1396
|
+
* their tool and RETURN, so `runInterpret` reaches the next Cmd of the turn
|
|
1397
|
+
* while the first tool is still running.
|
|
1398
|
+
*
|
|
1399
|
+
* This is ADR 0018's option (c), and the whole of it. `runInterpret` stays the
|
|
1400
|
+
* serial loop the ADR ruled it must be: it still awaits one handler before
|
|
1401
|
+
* reaching the next Cmd — the handlers just stop being the thing that takes the
|
|
1402
|
+
* time. Three properties are what make that safe, and each is a line below:
|
|
1403
|
+
*
|
|
1404
|
+
* - **Settles fold in Cmd-EMISSION order, never completion order.** Each cell
|
|
1405
|
+
* chains its dispatch behind the previous cell's, so two tools that overlap
|
|
1406
|
+
* on the clock still settle a-then-b when a's Cmd was emitted first, whichever
|
|
1407
|
+
* finished first. That is invariant 2's serializability, and it is what keeps
|
|
1408
|
+
* a replay of a fanned run identical to the run itself.
|
|
1409
|
+
* - **The settle crosses the SAME edge.** A returned Msg is parsed and stamped
|
|
1410
|
+
* by `run`'s `cmdEdge`; a dispatched one would not be, so the cell settles
|
|
1411
|
+
* through `cmdEdgeOf(ctx)` — the edge `run` hands handlers for exactly this.
|
|
1412
|
+
* One parse, one clock, fanned or not.
|
|
1413
|
+
* - **The work stays counted.** The promise the cell does not return is enlisted
|
|
1414
|
+
* with `detachWorkOf(ctx)`, so `inFlightCmds` and the dispatch tail see it and
|
|
1415
|
+
* `stop()` / `idle()` answer under fan-out what they answer serially.
|
|
1416
|
+
*
|
|
1417
|
+
* The ordering has a price, and it is the honest one: a call whose Cmd was
|
|
1418
|
+
* emitted FIRST holds its siblings' settles until it finishes. They still RUN
|
|
1419
|
+
* in parallel — the turn costs the slowest call, not the sum — but a first call
|
|
1420
|
+
* that never returns leaves the later folds parked behind it, exactly as a
|
|
1421
|
+
* serial run would leave them unstarted. `timeoutMs` on the tool is what bounds
|
|
1422
|
+
* that, fanned or not.
|
|
1423
|
+
*
|
|
1424
|
+
* The per-turn LIMIT is not this function's: the agent's fan-out slice already
|
|
1425
|
+
* launches at most `toolConcurrency` calls per transition, so the table simply
|
|
1426
|
+
* must not block. Nothing here is applied below that knob — `toMachine` wires
|
|
1427
|
+
* the bare cells at the serial default, and a tool that never overlaps is the
|
|
1428
|
+
* exact function `toolRouter` built.
|
|
1429
|
+
*/
|
|
1430
|
+
declare function fanOutInterpret<T extends AnyToolDef>(interpret: Interpret<ToolMsg<T>, ToolCmd<T>, NoCtx>): Interpret<ToolMsg<T>, ToolCmd<T>, NoCtx>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Turn a tool failure into the human-readable reason string the conversation
|
|
1433
|
+
* carries — pass the `{ _tag, ...detail }` a tool failed with, get the tag
|
|
1434
|
+
* followed by any remaining detail as JSON.
|
|
1435
|
+
*
|
|
1436
|
+
* The
|
|
1437
|
+
* model reads this next turn, so the data survives the rendering intact.
|
|
1438
|
+
*/
|
|
1439
|
+
declare function toolErrorReason(error: Tagged): string;
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* @demlik/tea/agent — the wired-machine surface.
|
|
1443
|
+
*
|
|
1444
|
+
* The Cmd / Msg vocabulary the agent speaks, the handle interface `createAgent`
|
|
1445
|
+
* returns (`AgentKnob` / `AgentToMachine`), the do↔agent boot port, the
|
|
1446
|
+
* semantic lifecycle event stream + its projector (`agentEvents`), and the two
|
|
1447
|
+
* pure wiring helpers `mergeInterpret` / `liftAgent`. The reducer core (the
|
|
1448
|
+
* `createAgent` factory that implements this surface) lives in `./index`.
|
|
1449
|
+
*/
|
|
1450
|
+
|
|
1451
|
+
/** The brain-call effect Cmd, inherited from `../llm-call`. */
|
|
1452
|
+
type AgentLlmRunCmd<P extends string> = LlmRunCmd<P>;
|
|
1453
|
+
/**
|
|
1454
|
+
* The Cmd union the agent emits, as a CLOSED discriminated union (precise `TC`,
|
|
1455
|
+
* not the open `Cmd`) so `Interpret<M, AgentCmd<P, TC>, Ctx>` maps each key
|
|
1456
|
+
* precisely and `toMachine` merges the interpret halves with no laundering cast:
|
|
1457
|
+
*
|
|
1458
|
+
* - `AgentLlmRunCmd<P>` — the brain-call run Cmd (`resilient_run`), folded
|
|
1459
|
+
* by the wired `brainHandlers` handler.
|
|
1460
|
+
* - `MonitoredRunCmd<unknown>` — the durable checkpoint write the monitored-run
|
|
1461
|
+
* slice emits, present in the union ONLY when
|
|
1462
|
+
* checkpointing is on (`Snap = true`). With
|
|
1463
|
+
* checkpointing off the monitored-run slice never
|
|
1464
|
+
* emits a `snapshot_write` Cmd, so it is config-derived
|
|
1465
|
+
* OUT of the emitted set — the consumer is never asked
|
|
1466
|
+
* to interpret a Cmd that can never fire.
|
|
1467
|
+
* - `TC` — the consumer's per-tool effect, mapped to the
|
|
1468
|
+
* consumer's own tool interpret handler.
|
|
1469
|
+
*
|
|
1470
|
+
* `Snap` defaults to `true` so the verb-internal usage (which forwards whatever
|
|
1471
|
+
* monitored-run emits) and the published type stay a safe superset; the wired
|
|
1472
|
+
* machine `toMachine` returns is parametrized on the overload-fixed `Snap` so its
|
|
1473
|
+
* Cmd set is exact (`MonitoredRunCmd` present only when checkpointing is on).
|
|
1474
|
+
*/
|
|
1475
|
+
type AgentCmd<P extends string, TC extends Cmd = Cmd, Snap extends boolean = true, Compact extends boolean = true> = AgentLlmRunCmd<P> | (Snap extends true ? MonitoredRunCmd<unknown> : never) | (Compact extends true ? AgentCompactRunCmd : never) | TC;
|
|
1476
|
+
/**
|
|
1477
|
+
* The CONFIG-DERIVED snapshot obligation on `toMachine`'s `toolInterpret`.
|
|
1478
|
+
* Resolves on the `Snap` discriminant the snapshotting overload of `createAgent`
|
|
1479
|
+
* fixes (`true` checkpointing-on / `false` off):
|
|
1480
|
+
*
|
|
1481
|
+
* - checkpointing ON → a REQUIRED `snapshot_write` handler
|
|
1482
|
+
* (`Interpret<M, MonitoredRunCmd<unknown>, Ctx>`). A real checkpoint write
|
|
1483
|
+
* MUST be wired — the agent never defaults it to a no-op.
|
|
1484
|
+
* - checkpointing OFF → `{ snapshot_write?: never }`. The handler is FORBIDDEN:
|
|
1485
|
+
* the Cmd is config-derived out of the emitted set, so wiring it would be
|
|
1486
|
+
* dead code. The consumer cannot even mention `snapshot_write`.
|
|
1487
|
+
*
|
|
1488
|
+
* This is the type that kills the `snapshot_write: async () => undefined`
|
|
1489
|
+
* ceremony: the obligation is present in the contract EXACTLY when the Cmd can
|
|
1490
|
+
* fire, never as a silent gap the agent backfills.
|
|
1491
|
+
*/
|
|
1492
|
+
type SnapshotInterpret<M extends {
|
|
1493
|
+
type: string;
|
|
1494
|
+
}, Snap extends boolean, Ctx> = Snap extends true ? Interpret<M, MonitoredRunCmd<unknown>, Ctx> : {
|
|
1495
|
+
readonly snapshot_write?: never;
|
|
1496
|
+
};
|
|
1497
|
+
/**
|
|
1498
|
+
* The `toMachine` signature, parametrized on the `Snap` + `Compact` discriminants
|
|
1499
|
+
* so the snapshotting / compaction overloads of `createAgent` hand back the right
|
|
1500
|
+
* obligations. The `toolInterpret` requires (ON) or forbids (OFF) the
|
|
1501
|
+
* `snapshot_write` handler via {@link SnapshotInterpret} and the `compact_run` handler
|
|
1502
|
+
* via {@link CompactInterpret}, and the machine's Cmd type is the config-derived
|
|
1503
|
+
* `AgentCmd<P, TC, Snap, Compact>`.
|
|
1504
|
+
*
|
|
1505
|
+
* `tools` is a `toolRouter`: its handlers are merged in, its settled
|
|
1506
|
+
* `<name>_ok` / `<name>_err` Msgs join the machine's `M` and fold into the
|
|
1507
|
+
* conversation, and its Cmds leave the `toolInterpret` obligation — a consumer
|
|
1508
|
+
* whose `toolOf` is the router's supplies only the snapshot / compaction handlers.
|
|
1509
|
+
* With no router (`T = never`) every type here reads exactly as before.
|
|
1510
|
+
*/
|
|
1511
|
+
type AgentToMachine<Stage, P extends string, O extends Record<P, AgentTurn>, R, TC extends Cmd, Snap extends boolean, Compact extends boolean> = <Ctx = object, T extends AnyToolDef = never>(opts?: {
|
|
1512
|
+
readonly tools?: ToolRouter<T>;
|
|
1513
|
+
readonly toolInterpret?: Interpret<AgentMachineMsg<P, O, R> | WiredToolMsg<T>, Exclude<TC, WiredToolCmd<T>>, Ctx> & SnapshotInterpret<AgentMachineMsg<P, O, R> | WiredToolMsg<T>, Snap, Ctx> & CompactInterpret<AgentMachineMsg<P, O, R> | WiredToolMsg<T>, Compact, Ctx>;
|
|
1514
|
+
}) => Machine<AgentState<Stage, P, O, R>, AgentMachineMsg<P, O, R> | WiredToolMsg<T>, AgentCmd<P, TC, Snap, Compact>, DeadlineSub, Ctx>;
|
|
1515
|
+
/**
|
|
1516
|
+
* The agent handle `createAgent` returns — the uniform verb contract every tea
|
|
1517
|
+
* composition exposes, plus the wired `toMachine` and the `unsafeDetachedHandlers`
|
|
1518
|
+
* escape hatch. `Snap` flows ONLY into `toMachine`'s `toolInterpret` obligation
|
|
1519
|
+
* (the snapshot derivation); every verb is snapshot-agnostic. The model
|
|
1520
|
+
* message shape `Msg` does not appear on the handle's surface (it is internal to the
|
|
1521
|
+
* brain call's loader), so it is not a type parameter here — only `createAgent`
|
|
1522
|
+
* carries it, to thread the config's `loadMessages` / `model` ports.
|
|
1523
|
+
*/
|
|
1524
|
+
interface AgentKnob<Stage, P extends string, O extends Record<P, AgentTurn>, R, TC extends Cmd, Snap extends boolean, Compact extends boolean> {
|
|
1525
|
+
readonly init: () => AgentState<Stage, P, O, R>;
|
|
1526
|
+
readonly start: AgentVerb1<Stage, P, O, R, TC, [runId: string, at: number]>;
|
|
1527
|
+
readonly turn: AgentVerb1<Stage, P, O, R, TC, [
|
|
1528
|
+
result: AgentTurn,
|
|
1529
|
+
at: number
|
|
1530
|
+
]>;
|
|
1531
|
+
readonly toolOk: AgentVerb1<Stage, P, O, R, TC, [
|
|
1532
|
+
callId: string,
|
|
1533
|
+
result: R,
|
|
1534
|
+
at: number
|
|
1535
|
+
]>;
|
|
1536
|
+
/**
|
|
1537
|
+
* A bare `reason` string still works and settles an untagged failure — the
|
|
1538
|
+
* hand-wired shape this verb has always had. Hand it a whole `ToolFailure`
|
|
1539
|
+
* instead to keep the `{ _tag, …payload }` beside that reason, which is what
|
|
1540
|
+
* `onToolError` and any host code discriminating on `_tag` then read (#115).
|
|
1541
|
+
*/
|
|
1542
|
+
readonly toolErr: AgentVerb1<Stage, P, O, R, TC, [
|
|
1543
|
+
callId: string,
|
|
1544
|
+
failure: string | ToolFailure,
|
|
1545
|
+
at: number
|
|
1546
|
+
]>;
|
|
1547
|
+
readonly succeed: AgentVerb1<Stage, P, O, R, TC, [
|
|
1548
|
+
key: string,
|
|
1549
|
+
msg: AgentLlmOkMsg<P, O>,
|
|
1550
|
+
at: number
|
|
1551
|
+
]>;
|
|
1552
|
+
readonly fail: AgentVerb1<Stage, P, O, R, TC, [
|
|
1553
|
+
key: string,
|
|
1554
|
+
msg: AgentLlmErrMsg<P>,
|
|
1555
|
+
at: number
|
|
1556
|
+
]>;
|
|
1557
|
+
readonly compactOk: AgentVerb1<Stage, P, O, R, TC, [
|
|
1558
|
+
key: string,
|
|
1559
|
+
msg: AgentCompactOkMsg,
|
|
1560
|
+
at: number
|
|
1561
|
+
]>;
|
|
1562
|
+
readonly compactErr: AgentVerb1<Stage, P, O, R, TC, [
|
|
1563
|
+
key: string,
|
|
1564
|
+
msg: AgentCompactErrMsg,
|
|
1565
|
+
at: number
|
|
1566
|
+
]>;
|
|
1567
|
+
readonly onTimer: AgentVerb1<Stage, P, O, R, TC, [msg: AgentTimerMsg]>;
|
|
1568
|
+
readonly boot: AgentVerb1<Stage, P, O, R, TC, [at: number]>;
|
|
1569
|
+
readonly isSettled: (s: AgentState<Stage, P, O, R>) => boolean;
|
|
1570
|
+
readonly currentStage: (s: AgentState<Stage, P, O, R>) => Stage | undefined;
|
|
1571
|
+
readonly brainCall: (s: AgentState<Stage, P, O, R>) => LlmCall<P>;
|
|
1572
|
+
readonly subs: (s: AgentState<Stage, P, O, R>) => readonly DeadlineSub[];
|
|
1573
|
+
readonly toMachine: AgentToMachine<Stage, P, O, R, TC, Snap, Compact>;
|
|
1574
|
+
readonly unsafeDetachedHandlers: <M>(ports: AgentPorts<P, O, M>) => AgentDetachedHandlers<P, M>;
|
|
1575
|
+
}
|
|
1576
|
+
/** A verb taking the state + `Args`, returning the agent's `[State, Cmd[]]` tuple. */
|
|
1577
|
+
type AgentVerb1<Stage, P extends string, O extends Record<P, AgentTurn>, R, TC extends Cmd, Args extends readonly unknown[]> = (s: AgentState<Stage, P, O, R>, ...args: Args) => readonly [AgentState<Stage, P, O, R>, readonly AgentCmd<P, TC>[]];
|
|
1578
|
+
/** The brain-call SUCCESS settle Msg, inherited from `../llm-call`. */
|
|
1579
|
+
type AgentLlmOkMsg<P extends string, O extends Record<P, unknown>> = LlmSucceedMsg<P, O>;
|
|
1580
|
+
/**
|
|
1581
|
+
* The brain-call FAILURE settle Msg, inherited from `../llm-call` — it re-enters
|
|
1582
|
+
* the agent's `fail` verb, which backs off via the retry ladder rather than
|
|
1583
|
+
* ending the run.
|
|
1584
|
+
*/
|
|
1585
|
+
type AgentLlmErrMsg<P extends string> = LlmFailMsg<P>;
|
|
1586
|
+
/**
|
|
1587
|
+
* The timer Msg (retry + safety deadline) — `DeadlineExceeded`, the shared
|
|
1588
|
+
* shape of both composed wrappers' timer Msgs (`LlmTimerMsg` and `MonitoredRunTimerMsg`
|
|
1589
|
+
* are both `DeadlineExceeded`). One Msg variant covers both timers; `onTimer`
|
|
1590
|
+
* disambiguates by Sub id. This is also the machine Msg the `subscribeDeadline`
|
|
1591
|
+
* handler dispatches directly (no wrapping), matching the sibling gold standard.
|
|
1592
|
+
*/
|
|
1593
|
+
type AgentTimerMsg = LlmTimerMsg;
|
|
1594
|
+
/** Ports the consumer supplies to the llm-call handler — re-exported shape. */
|
|
1595
|
+
type AgentPorts<P extends string, O extends Record<P, unknown>, M> = LlmCallPorts<P, O, M>;
|
|
1596
|
+
/**
|
|
1597
|
+
* The LEGACY detached brain-call handler dictionary `handlers(ports)` returns,
|
|
1598
|
+
* superseded by the `Interpret` table `AgentKnob.toMachine()` wires — reach for
|
|
1599
|
+
* `toMachine()` unless you are hand-wiring the verbs yourself.
|
|
1600
|
+
*
|
|
1601
|
+
* It is the inherited `../llm-call` detached form's exact shape, NOT an
|
|
1602
|
+
* `Interpret`.
|
|
1603
|
+
* The `resilient_run` handler runs the invoke inside `ctx.waitUntil` and dispatches
|
|
1604
|
+
* the consumer's `onOk` / `onErr` Msg directly (returning `void`), so it is a
|
|
1605
|
+
* fire-and-forget handler with a structural `{ waitUntil, dispatch }` ctx — it
|
|
1606
|
+
* does not re-enter the resilient settle Msg and so does not drive the retry
|
|
1607
|
+
* loop. Naming the type precisely (rather than laundering it through
|
|
1608
|
+
* `as unknown as Interpret<...>`) keeps `agent.unsafeDetachedHandlers(ports)` honest: the
|
|
1609
|
+
* consumer that wires the verbs by hand gets the real detached shape, and its
|
|
1610
|
+
* `resilient_run` is callable with a plain Cmd + a `{ waitUntil, dispatch }` ctx
|
|
1611
|
+
* with no `as never` at the call site.
|
|
1612
|
+
*/
|
|
1613
|
+
type AgentDetachedHandlers<P extends string, M> = {
|
|
1614
|
+
readonly resilient_run: (cmd: AgentLlmRunCmd<P>, ctx: {
|
|
1615
|
+
waitUntil(p: Promise<unknown>): void;
|
|
1616
|
+
dispatch(msg: M): unknown;
|
|
1617
|
+
}) => void;
|
|
1618
|
+
};
|
|
1619
|
+
/** The Msg `do/host`'s `autoBoot` fires to re-enter the agent's `boot` verb on rehydrate. */
|
|
1620
|
+
type AgentBootMsg = {
|
|
1621
|
+
readonly type: typeof MsgType.AgentBoot;
|
|
1622
|
+
readonly at: number;
|
|
1623
|
+
};
|
|
1624
|
+
/**
|
|
1625
|
+
* The do↔agent boot port: construct the `agent_boot` Msg `autoBoot` dispatches
|
|
1626
|
+
* on a resumable rehydrate. The agent owns this constructor so the boot Msg
|
|
1627
|
+
* shape lives in ONE place — the host fires it through here, never as a raw
|
|
1628
|
+
* `{ type: "agent_boot" }` literal.
|
|
1629
|
+
*/
|
|
1630
|
+
declare function agentBootMsg(at: number): AgentBootMsg;
|
|
1631
|
+
/**
|
|
1632
|
+
* The Msg an aborted `AbortSignal` fires to settle the run `cancelled`. Owned
|
|
1633
|
+
* here beside the boot Msg for the same reason: the cancel seam lives on the
|
|
1634
|
+
* drive (`driveToDone`'s `cancel`, the lid's `signal`), which is a different
|
|
1635
|
+
* module from the reducer that folds it, so the shape gets ONE home and a
|
|
1636
|
+
* constructor rather than a literal duplicated across the seam.
|
|
1637
|
+
*/
|
|
1638
|
+
type AgentCancelMsg = {
|
|
1639
|
+
readonly type: typeof MsgType.AgentCancel;
|
|
1640
|
+
readonly at: number;
|
|
1641
|
+
};
|
|
1642
|
+
/** Construct the `agent_cancel` Msg an abort dispatches. `at` stamps the outcome. */
|
|
1643
|
+
declare function agentCancelMsg(at: number): AgentCancelMsg;
|
|
1644
|
+
/**
|
|
1645
|
+
* The agent machine's Msg union — one variant per reducer entry point. A
|
|
1646
|
+
* consumer using `toMachine` dispatches `agent_start` to begin and `agent_tool_ok`
|
|
1647
|
+
* / `agent_tool_err` to route settled tools back; the brain-call settle Msgs
|
|
1648
|
+
* (`resilient_ok` / `resilient_err`) RE-ENTER from the FIXED `brainHandlers`
|
|
1649
|
+
* (the substrate enqueues an interpret handler's returned Msg as a follow-up),
|
|
1650
|
+
* driving `succeed` / `fail`. Time enters via `at` on every variant — the
|
|
1651
|
+
* reducer never reads the clock.
|
|
1652
|
+
*
|
|
1653
|
+
* There is deliberately NO `agent_turn` variant here. The wired loop folds a
|
|
1654
|
+
* model turn INSIDE `succeed` from the re-entered `resilient_ok` — a single
|
|
1655
|
+
* settle Msg advances both the retry slice and the conversation (the L3 fix).
|
|
1656
|
+
* Exposing `agent_turn` as a dispatchable Msg re-opened the stuck-`running` bug:
|
|
1657
|
+
* a hand-fed turn folds the conversation without ever re-entering `resilient_ok`,
|
|
1658
|
+
* so `succeed` never runs and the resilient slice stays `running`. The
|
|
1659
|
+
* `turn` verb remains on the handle for the consumer that wires the verbs by hand
|
|
1660
|
+
* (manual wiring), but it is not part of the one wired machine's Msg surface.
|
|
1661
|
+
*/
|
|
1662
|
+
type AgentMachineMsg<P extends string, O extends Record<P, unknown>, R> = {
|
|
1663
|
+
readonly type: typeof MsgType.AgentStart;
|
|
1664
|
+
readonly runId: string;
|
|
1665
|
+
readonly at: number;
|
|
1666
|
+
} | {
|
|
1667
|
+
readonly type: typeof MsgType.AgentToolOk;
|
|
1668
|
+
readonly callId: string;
|
|
1669
|
+
readonly result: R;
|
|
1670
|
+
readonly at: number;
|
|
1671
|
+
} | {
|
|
1672
|
+
readonly type: typeof MsgType.AgentToolErr;
|
|
1673
|
+
readonly callId: string;
|
|
1674
|
+
readonly reason: string;
|
|
1675
|
+
readonly at: number;
|
|
1676
|
+
} | AgentLlmOkMsg<P, O> | AgentLlmErrMsg<P> | AgentCompactOkMsg | AgentCompactErrMsg | AgentTimerMsg | AgentBootMsg | AgentCancelMsg;
|
|
1677
|
+
/**
|
|
1678
|
+
* The agent's PUBLIC lifecycle events — the semantic stream a consumer
|
|
1679
|
+
* subscribes to via `runtime.on(type, …)`. This is the seam that
|
|
1680
|
+
* DECOUPLES observability/SSE code from the agent's PRIVATE retry/loop Msg
|
|
1681
|
+
* vocabulary: where the old code switched on `resilient_ok` / `agent_tool_ok`
|
|
1682
|
+
* (the inherited resilient-call / tool-fan-out plumbing) off the raw `observe`
|
|
1683
|
+
* firehose, a consumer now folds these named events. The private Msg names live
|
|
1684
|
+
* ONLY inside `agentEvents` (the projector) — they appear nowhere in this union
|
|
1685
|
+
* or any exported type, so a UI built on `TurnSettled` does not break the day
|
|
1686
|
+
* the retry plumbing is renamed.
|
|
1687
|
+
*
|
|
1688
|
+
* - `TurnSettled` — a brain turn settled (the model produced an `AgentTurn`).
|
|
1689
|
+
* Projected off the private `resilient_ok` settle Msg; carries the parsed
|
|
1690
|
+
* `turn` (the narration + tool calls the model asked for).
|
|
1691
|
+
* - `ToolSettled` — a tool call settled OK. Projected off the private
|
|
1692
|
+
* `agent_tool_ok` Msg; carries the `callId` and the tool `result`.
|
|
1693
|
+
* - `RunDone` — the run finished. Projected off the post-transition State
|
|
1694
|
+
* reaching `run.phase === "done"`; carries the run's `output` (the terminal
|
|
1695
|
+
* model turn, or `null`) — the same first-class result `Runtime.result()`
|
|
1696
|
+
* reads (#46), surfaced as an event for the streaming consumer.
|
|
1697
|
+
*/
|
|
1698
|
+
type AgentEvent<R> = {
|
|
1699
|
+
readonly type: "TurnSettled";
|
|
1700
|
+
readonly turn: AgentTurn;
|
|
1701
|
+
} | {
|
|
1702
|
+
readonly type: "ToolSettled";
|
|
1703
|
+
readonly callId: string;
|
|
1704
|
+
readonly result: R;
|
|
1705
|
+
} | {
|
|
1706
|
+
readonly type: "RunDone";
|
|
1707
|
+
readonly output: AgentTurn | null;
|
|
1708
|
+
};
|
|
1709
|
+
/**
|
|
1710
|
+
* Project one APPLIED agent transition `(msg, state)` to its semantic
|
|
1711
|
+
* {@link AgentEvent}s — the `events` projector a consumer passes to
|
|
1712
|
+
* `run(machine, { events: agentEvents() })` to light up `runtime.on(...)`.
|
|
1713
|
+
*
|
|
1714
|
+
* This is the ONE place the agent's PRIVATE Msg names are read: it maps
|
|
1715
|
+
* `resilient_ok` → `TurnSettled` and `agent_tool_ok` → `ToolSettled`, and reads
|
|
1716
|
+
* the post-transition `run.phase` for `RunDone`. The mapping is total over the
|
|
1717
|
+
* Msg union (a `default`-free switch on the discriminant) and returns `[]` for
|
|
1718
|
+
* the transitions that carry no public event (start / boot / timer / the error
|
|
1719
|
+
* arms), so a private name never escapes into `AgentEvent`.
|
|
1720
|
+
*
|
|
1721
|
+
* A single transition may emit more than one event: the brain turn that retires
|
|
1722
|
+
* the final stage settles a turn (`TurnSettled`) AND finishes the run
|
|
1723
|
+
* (`RunDone`) — both are projected from that one `resilient_ok` transition.
|
|
1724
|
+
* `RunDone` is gated on `run.phase === "done"`, which the wired loop reaches
|
|
1725
|
+
* exactly once (the agent is terminal there and dispatches no further
|
|
1726
|
+
* transition), so the event fires once per run.
|
|
1727
|
+
*
|
|
1728
|
+
* A `ToolSettled` is gated on `state.refusedCalls` (#145): a call whose public
|
|
1729
|
+
* outcome was already a failure — the ladder's `timeout` / `retry_exhausted`,
|
|
1730
|
+
* or the tool's own error — never emits a success here, however late its
|
|
1731
|
+
* abandoned attempt resolves. Both settle arms read the same list, so a
|
|
1732
|
+
* router-wired agent and a fan-out-wired one are silent on the same calls, and
|
|
1733
|
+
* the decision is made in the PURE layer rather than filtered at a
|
|
1734
|
+
* subscription seam — `runtime.on` and `defineAgent`'s `onEvent` see one
|
|
1735
|
+
* stream.
|
|
1736
|
+
*
|
|
1737
|
+
* A machine wired with `toMachine({ tools })` settles tools through the
|
|
1738
|
+
* router's `<name>_ok` Msgs instead of `agent_tool_ok`; pass the same router
|
|
1739
|
+
* here so those settles project to `ToolSettled` too.
|
|
1740
|
+
*
|
|
1741
|
+
* PURE — reads only the passed `(msg, state)`; no clock, no RNG, no throw.
|
|
1742
|
+
*
|
|
1743
|
+
* @typeParam P Brain-call purposes.
|
|
1744
|
+
* @typeParam O Per-purpose outputs (bound to `AgentTurn`, the agentic shape).
|
|
1745
|
+
* @typeParam R Tool result type.
|
|
1746
|
+
* @typeParam Stage Pipeline stage type.
|
|
1747
|
+
* @typeParam T The router's tool defs, when one is wired.
|
|
1748
|
+
*/
|
|
1749
|
+
declare function agentEvents<Stage, P extends string, O extends Record<P, AgentTurn>, R, T extends AnyToolDef = never>(opts?: {
|
|
1750
|
+
readonly tools?: ToolRouter<T>;
|
|
1751
|
+
}): (msg: AgentMachineMsg<P, O, R> | WiredToolMsg<T>, state: AgentState<Stage, P, O, R>) => readonly AgentEvent<R>[];
|
|
1752
|
+
/**
|
|
1753
|
+
* Join two `Interpret` dictionaries over DISJOINT Cmd subsets `A` and `B` (over
|
|
1754
|
+
* the same Msg union `M` and Ctx) into the full `Interpret<M, A | B, Ctx>`. The
|
|
1755
|
+
* agent uses it to merge the consumer's per-tool / snapshot interpret (`A`) with
|
|
1756
|
+
* the agent-owned brain-call handler (`B = AgentLlmRunCmd`) into the wired
|
|
1757
|
+
* machine's single interpret table.
|
|
1758
|
+
*
|
|
1759
|
+
* The runtime result is exactly the spread of the two key-disjoint records. The
|
|
1760
|
+
* single `as` is a SOUND mapped-type identity, not a laundering cast: `Interpret`
|
|
1761
|
+
* is a homomorphic mapped type keyed by `Cmd["type"]`, so
|
|
1762
|
+
* `Interpret<M, A, Ctx> & Interpret<M, B, Ctx>` IS `Interpret<M, A | B, Ctx>`
|
|
1763
|
+
* (`(A | B)["type"] = A["type"] | B["type"]`). TypeScript does not reduce that
|
|
1764
|
+
* intersection of two GENERIC mapped types on its own, so the equality is
|
|
1765
|
+
* asserted ONCE here — preserving `M` and `Ctx` exactly — rather than smuggled
|
|
1766
|
+
* through `as unknown as Interpret<...>` at every wiring site. Pure.
|
|
1767
|
+
*/
|
|
1768
|
+
declare function mergeInterpret<M extends {
|
|
1769
|
+
type: string;
|
|
1770
|
+
}, A extends Cmd, B extends Cmd, Ctx>(a: Interpret<M, A, Ctx> | undefined, b: Interpret<M, B, Ctx>): Interpret<M, A | B, Ctx>;
|
|
1771
|
+
/**
|
|
1772
|
+
* Lift an agent result `[slice, cmds]` into a host `[State, cmds]` where the slice
|
|
1773
|
+
* lives at `state.agent`. Convenience for a host that nests the agent slice
|
|
1774
|
+
* under a named field; the single-slice host uses the verbs' tuple directly.
|
|
1775
|
+
* PURE — a thin record rebuild, no clock / RNG.
|
|
1776
|
+
*/
|
|
1777
|
+
declare function liftAgent<S extends {
|
|
1778
|
+
agent: AgentState<Stage, P, O, R>;
|
|
1779
|
+
}, Stage, P extends string, O extends Record<P, unknown>, R, C extends Cmd>(state: S, [slice, cmds]: readonly [AgentState<Stage, P, O, R>, readonly C[]]): readonly [S, readonly C[]];
|
|
1780
|
+
|
|
1781
|
+
export { PLAIN_MODEL_MISROUTE_REASON as $, type AnyToolDef as A, type AgentSnapshotConfig as B, type Conversation as C, type AgentStatus as D, type AgentTerminalFailure as E, type AgentTimerMsg as F, type AgentToMachine as G, type Awaiting as H, COMPACTION_PURPOSE as I, type CompactInterpret as J, type CompactionOutputs as K, type CompactionPurpose as L, type CompactionSummary as M, type LlmCall as N, type LlmErr as O, type PlainModel as P, type LlmFailMsg as Q, type LlmOk as R, type StreamingModel as S, type ToolCall as T, type LlmRunCmd as U, type LlmSucceedMsg as V, type WiredToolMsg as W, type MessageLoader as X, type ModelFactory as Y, type ModelPort as Z, type ModelStream as _, type ToolOutcome as a, type ReservedToolName as a0, type Schema as a1, type SnapshotInterpret as a2, TOOL_RETRY_EXHAUSTED_TAG as a3, TOOL_TIMEOUT_TAG as a4, type TaggedFailure as a5, type ToolConstructors as a6, type ToolDef as a7, type ToolError as a8, type ToolFail as a9, liftAgent as aA, mergeInterpret as aB, plainModel as aC, status as aD, tool as aE, toolErrorReason as aF, toolRouter as aG, type ToolFailure as aa, type ToolHandler as ab, type ToolInput as ac, type ToolMsg as ad, type ToolOk as ae, type ToolRecord as af, type ToolRejectedCmd as ag, type ToolRejection as ah, type ToolResilience as ai, type ToolResilienceError as aj, type ToolRetryExhausted as ak, type ToolRouter as al, type ToolSettlement as am, type ToolThrown as an, type ToolTimedOut as ao, type WiredToolCmd as ap, agentBootMsg as aq, agentCancelMsg as ar, agentEvents as as, agentTurnSchema as at, compactionSummarySchema as au, fanOutInterpret as av, isAgentTurn as aw, isCompactionSummary as ax, isReservedToolName as ay, isStreamingModel as az, type AgentTurn as b, type AgentState as c, type ToolResult as d, type ToolFailureOf as e, type ToolCmd as f, type AgentEvent as g, type TurnChunk as h, type AgentMachineMsg as i, type AgentCmd as j, type AgentConfigCore as k, type CompactionPolicy as l, type AgentKnob as m, type AgentBootMsg as n, type AgentCancelMsg as o, type AgentCompactErrMsg as p, type AgentCompactOkMsg as q, type AgentCompactRunCmd as r, type AgentCompactionConfig as s, type AgentConfig as t, type AgentDetachedHandlers as u, type AgentFailure as v, type AgentLlmErrMsg as w, type AgentLlmOkMsg as x, type AgentLlmRunCmd as y, type AgentPorts as z };
|