@earendil-works/pi-agent-core 0.84.1 → 0.84.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/harness/events.d.ts +43 -0
  2. package/dist/harness/events.d.ts.map +1 -0
  3. package/dist/harness/events.js +65 -0
  4. package/dist/harness/events.js.map +1 -0
  5. package/dist/harness/session/jsonl/codec.d.ts +4 -2
  6. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  7. package/dist/harness/session/jsonl/codec.js +127 -88
  8. package/dist/harness/session/jsonl/codec.js.map +1 -1
  9. package/dist/harness/session/jsonl/errors.d.ts +5 -1
  10. package/dist/harness/session/jsonl/errors.d.ts.map +1 -1
  11. package/dist/harness/session/jsonl/errors.js +10 -2
  12. package/dist/harness/session/jsonl/errors.js.map +1 -1
  13. package/dist/harness/session/jsonl/repo.d.ts +7 -3
  14. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  15. package/dist/harness/session/jsonl/repo.js +88 -45
  16. package/dist/harness/session/jsonl/repo.js.map +1 -1
  17. package/dist/harness/session/jsonl/storage.d.ts +1 -1
  18. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  19. package/dist/harness/session/jsonl/storage.js +28 -19
  20. package/dist/harness/session/jsonl/storage.js.map +1 -1
  21. package/dist/harness/session/jsonl/types.d.ts +1 -1
  22. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  23. package/dist/harness/session/jsonl/types.js.map +1 -1
  24. package/dist/harness/session/memory.d.ts +1 -1
  25. package/dist/harness/session/memory.d.ts.map +1 -1
  26. package/dist/harness/session/memory.js.map +1 -1
  27. package/dist/harness/session/session.d.ts +1 -1
  28. package/dist/harness/session/session.d.ts.map +1 -1
  29. package/dist/harness/session/session.js +7 -2
  30. package/dist/harness/session/session.js.map +1 -1
  31. package/dist/harness/session/state.d.ts +1 -1
  32. package/dist/harness/session/state.d.ts.map +1 -1
  33. package/dist/harness/session/state.js.map +1 -1
  34. package/dist/harness/session/testing/conformance.d.ts.map +1 -1
  35. package/dist/harness/session/testing/conformance.js +19 -0
  36. package/dist/harness/session/testing/conformance.js.map +1 -1
  37. package/dist/harness/session/types.d.ts +18 -5
  38. package/dist/harness/session/types.d.ts.map +1 -1
  39. package/dist/harness/session/types.js.map +1 -1
  40. package/dist/harness/skills.d.ts +3 -2
  41. package/dist/harness/skills.d.ts.map +1 -1
  42. package/dist/harness/skills.js +13 -3
  43. package/dist/harness/skills.js.map +1 -1
  44. package/dist/harness/tools/edit.d.ts.map +1 -1
  45. package/dist/harness/tools/edit.js +14 -1
  46. package/dist/harness/tools/edit.js.map +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/proxy.d.ts +2 -1
  52. package/dist/proxy.d.ts.map +1 -1
  53. package/dist/proxy.js +1 -0
  54. package/dist/proxy.js.map +1 -1
  55. package/dist/search/index.d.ts +21 -0
  56. package/dist/search/index.d.ts.map +1 -0
  57. package/dist/search/index.js +2 -0
  58. package/dist/search/index.js.map +1 -0
  59. package/dist/search/scanning.d.ts +29 -0
  60. package/dist/search/scanning.d.ts.map +1 -0
  61. package/dist/search/scanning.js +103 -0
  62. package/dist/search/scanning.js.map +1 -0
  63. package/package.json +4 -4
  64. package/dist/harness/session/search.d.ts +0 -19
  65. package/dist/harness/session/search.d.ts.map +0 -1
  66. package/dist/harness/session/search.js +0 -42
  67. package/dist/harness/session/search.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/harness/session/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAYN,YAAY,GAEZ,MAAM,YAAY,CAAC;AAWpB,SAAS,eAAe,CAAC,OAAe,EAAS;IAChD,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,6BAA6B,OAAO,EAAE,CAAC,CAAC;AAAA,CAChF;AAED,SAAS,gBAAgB,CAAC,KAAyB,EAAQ;IAC1D,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,kCAAkC,CAAC,CAAC;IAC7E,CAAC;AAAA,CACD;AAED,SAAS,iBAAiB,CAAC,QAA4B,EAAQ;IAC9D,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,gDAAgD,CAAC,CAAC;IAC3F,CAAC;AAAA,CACD;AAED,QAAQ,CAAC,CAAC,OAAO,CAAI,KAAmB,EAAE,KAA6B,EAAuB;IAC7F,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;QAC7B,KAAK,CAAC,CAAC,KAAK,CAAC;QACb,OAAO;IACR,CAAC;IACD,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE;QAAE,MAAM,KAAK,CAAC,KAAK,CAAE,CAAC;AAAA,CAC5E;AAED,MAAM,OAAO,YAAY;IAChB,QAAQ,GAAG,CAAC,CAAC;IACJ,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5B,OAAO,GAAY,EAAE,CAAC;IACtB,WAAW,GAAG,IAAI,GAAG,EAAiB,CAAC;IACvC,OAAO,GAAiB,EAAE,CAAC;IAC3B,oBAAoB,GAAG,IAAI,GAAG,EAA+C,CAAC;IAC9E,KAAK,GAAG,IAAI,GAAG,CAAwB,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACzD,GAAG,GAAc,EAAE,CAAC;IACpB,KAAK,GAAiB;QACtC,YAAY,EAAE,CAAC;QACf,YAAY,EAAE,CAAC;QACf,cAAc,EAAE,CAAC;QACjB,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,CAAC;KACZ,CAAC;IACM,IAAI,CAAqB;IAChB,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEpD,IAAI,YAAY,GAAW;QAC1B,OAAO,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IAAA,CACzB;IAED,QAAQ,GAAkB;QACzB,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAAA,CACnE;IAED,WAAW,CAAC,IAAY,EAAiB;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC;QAC5F,OAAO,MAAM,CAAC;IAAA,CACd;IAED,eAAe,CAAC,IAAY,EAAQ;QACnC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,gBAAgB,EAAE,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAAA,CACnG;IAED,cAAc,CAAC,QAAuB,EAAQ;QAC7C,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,oBAAoB,QAAQ,EAAE,CAAC,CAAC;QACrE,CAAC;IAAA,CACD;IAED,gBAAgB,CAAC,EAAU,EAAQ;QAClC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,gBAAgB,EAAE,8BAA8B,EAAE,EAAE,CAAC,CAAC;IAAA,CACvG;IAED,aAAa,CAAC,QAAyB,EAAE,OAAO,GAAoB,eAAe,EAAQ;QAC1F,MAAM,GAAG,GACR,QAAQ,CAAC,IAAI,KAAK,OAAO;YACxB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;YACpB,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,QAAQ;gBAC3B,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG;gBACrB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAClB,IAAI,GAAG,KAAK,IAAI,CAAC,QAAQ,GAAG,CAAC;YAAE,OAAO,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;QAEzE,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;YACvB,KAAK,OAAO,EAAE,CAAC;gBACd,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBAAE,OAAO,CAAC,yBAAyB,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/F,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBAC7C,IAAI,MAAM,KAAK,SAAS;wBAAE,OAAO,CAAC,2BAA2B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC9E,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,KAAK,MAAM;wBAAE,OAAO,CAAC,iCAAiC,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACxF,OAAO,CAAC,6BAA6B,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACjE,CAAC;gBACD,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACxD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;oBAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAClF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC7D,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;oBAAE,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;gBACpE,MAAM;YACP,CAAC;YACD,KAAK,QAAQ,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;oBAAE,OAAO,CAAC,2BAA2B,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtG,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBAAE,OAAO,CAAC,yBAAyB,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjG,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACnC,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;oBAClD,IAAI,cAAc,GAAG,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACzE,IAAI,CAAC,cAAc,EAAE,CAAC;wBACrB,cAAc,GAAG,IAAI,GAAG,EAAE,CAAC;wBAC3B,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBACrE,CAAC;oBACD,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACzD,CAAC;qBAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;oBAC1D,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAChE,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACtC,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;oBAC3D,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;oBAC5F,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;oBAC5D,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC1D,CAAC;gBACD,MAAM;YACP,CAAC;YACD,KAAK,MAAM;gBACV,IAAI,QAAQ,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxE,OAAO,CAAC,kCAAkC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9D,CAAC;gBACD,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC/C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACnF,MAAM;YACP,KAAK,MAAM;gBACV,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3E,OAAO,CAAC,mCAAmC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACjE,CAAC;gBACD,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBACpB,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC9B,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;oBAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;gBACzE,CAAC;qBAAM,CAAC;oBACP,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS;wBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;;wBACnE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;oBACxD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;wBACb,IAAI,EAAE,MAAM;wBACZ,GAAG;wBACH,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,QAAQ,CAAC,QAAQ;wBAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;qBACrB,CAAC,CAAC;gBACJ,CAAC;gBACD,MAAM;QACR,CAAC;IAAA,CACD;IAED,QAAQ,CAAC,EAAU,EAAqB;QACvC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAAA,CAChC;IAED,WAAW,CAAC,KAAK,GAAe,EAAE,EAAW;QAC5C,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9B,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAY,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;gBAAE,SAAS;YACpD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK;gBAAE,MAAM;QAC3C,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAED,mBAAmB,CAAC,KAAoD,EAAW;QAClF,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9B,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAY,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;gBACjE,MAAM,YAAY,GAAG,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,UAAU,CAAC;gBACpF,IAAI,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC9D,IAAI,YAAY,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK;oBAAE,MAAM;YAC3D,CAAC;QACF,CAAC;aAAM,CAAC;YACP,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;gBACzD,IAAI,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC9D,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK;oBAAE,MAAM;YAC3C,CAAC;QACF,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAED,WAAW,CAAC,KAAK,GAAgB,EAAE,EAAgB;QAClD,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9B,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,OAAO,GAAiB,EAAE,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC;gBAAE,SAAS;YACtD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK;gBAAE,MAAM;QAC3C,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAED,kBAAkB,CAAC,IAAY,EAAE,OAA4B,EAA4B;QACxF,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,cAAc,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,OAAO,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAAA,CAC9F;IAED,MAAM,CAAC,OAAO,GAAe,EAAE,EAAa;QAC3C,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,iBAAiB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,OAAO,GAAc,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,QAAQ;gBAAE,SAAS;YAC7E,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK;gBAAE,MAAM;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAED,OAAO,GAAuB;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC;IAAA,CACjB;IAED,QAAQ,CAAC,EAAU,EAAsB;QACxC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAAA,CAC3B;IAED,QAAQ,GAAiB;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC;IAAA,CAClB;IAED,mBAAmB,CAAC,OAAoB,EAAqB;QAC5D,IAAI,aAAsB,CAAC;QAC3B,IAAI,SAAwB,CAAC;QAC7B,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC9B,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;YAC3D,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;aAAM,CAAC;YACP,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACpE,IAAI,QAAQ,GAAkB,IAAI,CAAC;YACnC,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;gBAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;gBAC7C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACxC,MAAM,IAAI,YAAY,CAAC,qBAAqB,EAAE,uCAAuC,eAAe,EAAE,CAAC,CAAC;gBACzG,CAAC;gBACD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBACvF,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC1D,CAAC;YACD,aAAa,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;YAC7G,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,SAAS,GAAsB,EAAE,CAAC;QACxC,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,WAAW,IAAI,aAAa,EAAE,CAAC;YACzC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,eAAe,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;YACjC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7F,CAAC;QACF,CAAC;QACD,OAAO,SAAS,CAAC;IAAA,CACjB;IAEO,CAAC,UAAU,CAClB,KAAoB,EACpB,MAAsD,EAC5B;QAC1B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,IAAI,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,oBAAoB,KAAK,EAAE,CAAC,CAAC;QAC/E,OAAO,OAAO,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,sCAAsC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7F,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACxB,MAAM,OAAO,CAAC;YACd,IAAI,OAAO,CAAC,EAAE,KAAK,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI;gBAAE,MAAM;YAC/G,MAAM,QAAQ,GAAW,OAAO,CAAC,QAAQ,CAAC;YAC1C,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,oBAAoB,QAAQ,EAAE,CAAC,CAAC;QACvF,CAAC;IAAA,CACD;IAEO,iBAAiB,CAAC,KAAY,EAAE,KAAiB,EAAW;QACnE,OAAO,CACN,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;YACvD,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC;YACtG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;gBAC1B,CAAC,KAAK,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CACzG,CAAC;IAAA,CACF;IAEO,kBAAkB,CAAC,MAAkB,EAAE,KAAkB,EAAW;QAC3E,OAAO,CACN,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;YACxD,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;YACxD,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;gBACzB,CAAC,MAAM,CAAC,IAAI,KAAK,mBAAmB;oBACnC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK;oBAC3B,CAAC,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC;YACvD,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS;gBACjC,CAAC,MAAM,CAAC,IAAI,KAAK,mBAAmB,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC;YACrF,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,CAC7D,CAAC;IAAA,CACF;CACD","sourcesContent":["import {\n\ttype BranchBounds,\n\ttype Entry,\n\ttype EntryOrder,\n\ttype EntryQuery,\n\ttype ForkOptions,\n\ttype LanePointer,\n\ttype LaneRecord,\n\ttype LogItem,\n\ttype LogOptions,\n\ttype OperationStartedRecord,\n\ttype RecordQuery,\n\tSessionError,\n\ttype SessionStats,\n} from \"./types.ts\";\n\nexport type SessionMutation =\n\t| { kind: \"entry\"; lane?: string; entry: Entry }\n\t| { kind: \"record\"; record: LaneRecord }\n\t| { kind: \"lane\"; seq: number; lane: string; leafId: string | null }\n\t| { kind: \"fact\"; seq: number; fact: \"name\"; name: string }\n\t| { kind: \"fact\"; seq: number; fact: \"label\"; targetId: string; label: string | undefined };\n\ntype InvalidMutation = (message: string) => never;\n\nfunction invalidMutation(message: string): never {\n\tthrow new SessionError(\"invalid_entry\", `Invalid session mutation: ${message}`);\n}\n\nfunction assertValidLimit(limit: number | undefined): void {\n\tif (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"limit must be a positive integer\");\n\t}\n}\n\nfunction assertValidCursor(afterSeq: number | undefined): void {\n\tif (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"cursor sequence must be a non-negative integer\");\n\t}\n}\n\nfunction* ordered<T>(items: readonly T[], order: EntryOrder | undefined): IterableIterator<T> {\n\tif (order === \"oldestFirst\") {\n\t\tyield* items;\n\t\treturn;\n\t}\n\tfor (let index = items.length - 1; index >= 0; index--) yield items[index]!;\n}\n\nexport class SessionState {\n\tprivate sequence = 0;\n\tprivate readonly usedIds = new Set<string>();\n\tprivate readonly entries: Entry[] = [];\n\tprivate readonly entriesById = new Map<string, Entry>();\n\tprivate readonly records: LaneRecord[] = [];\n\tprivate readonly openOperationsByLane = new Map<string, Map<string, OperationStartedRecord>>();\n\tprivate readonly lanes = new Map<string, string | null>([[\"main\", null]]);\n\tprivate readonly log: LogItem[] = [];\n\tprivate readonly stats: SessionStats = {\n\t\tmessageCount: 0,\n\t\tcachedTokens: 0,\n\t\tuncachedTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcostTotal: 0,\n\t};\n\tprivate name: string | undefined;\n\tprivate readonly labels = new Map<string, string>();\n\n\tget nextSequence(): number {\n\t\treturn this.sequence + 1;\n\t}\n\n\tgetLanes(): LanePointer[] {\n\t\treturn [...this.lanes].map(([lane, leafId]) => ({ lane, leafId }));\n\t}\n\n\trequireLane(lane: string): string | null {\n\t\tconst leafId = this.lanes.get(lane);\n\t\tif (leafId === undefined) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\t\treturn leafId;\n\t}\n\n\tvalidateNewLane(lane: string): void {\n\t\tif (this.lanes.has(lane)) throw new SessionError(\"already_exists\", `Lane already exists: ${lane}`);\n\t}\n\n\tvalidateTarget(targetId: string | null): void {\n\t\tif (targetId !== null && !this.entriesById.has(targetId)) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry not found: ${targetId}`);\n\t\t}\n\t}\n\n\tvalidateUnusedId(id: string): void {\n\t\tif (this.usedIds.has(id)) throw new SessionError(\"already_exists\", `Session id already exists: ${id}`);\n\t}\n\n\tapplyMutation(mutation: SessionMutation, invalid: InvalidMutation = invalidMutation): void {\n\t\tconst seq =\n\t\t\tmutation.kind === \"entry\"\n\t\t\t\t? mutation.entry.seq\n\t\t\t\t: mutation.kind === \"record\"\n\t\t\t\t\t? mutation.record.seq\n\t\t\t\t\t: mutation.seq;\n\t\tif (seq !== this.sequence + 1) invalid(`has non-consecutive seq ${seq}`);\n\n\t\tswitch (mutation.kind) {\n\t\t\tcase \"entry\": {\n\t\t\t\tif (this.usedIds.has(mutation.entry.id)) invalid(`contains duplicate id ${mutation.entry.id}`);\n\t\t\t\tif (mutation.lane !== undefined) {\n\t\t\t\t\tconst leafId = this.lanes.get(mutation.lane);\n\t\t\t\t\tif (leafId === undefined) invalid(`references missing lane ${mutation.lane}`);\n\t\t\t\t\tif (mutation.entry.parentId !== leafId) invalid(\"does not chain to the lane leaf\");\n\t\t\t\t}\n\t\t\t\tif (mutation.entry.parentId !== null && !this.entriesById.has(mutation.entry.parentId)) {\n\t\t\t\t\tinvalid(`references missing parent ${mutation.entry.parentId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.usedIds.add(mutation.entry.id);\n\t\t\t\tthis.entries.push(mutation.entry);\n\t\t\t\tthis.entriesById.set(mutation.entry.id, mutation.entry);\n\t\t\t\tif (mutation.lane !== undefined) this.lanes.set(mutation.lane, mutation.entry.id);\n\t\t\t\tthis.log.push({ kind: \"entry\", seq, entry: mutation.entry });\n\t\t\t\tif (mutation.entry.type === \"message\") this.stats.messageCount += 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"record\": {\n\t\t\t\tif (!this.lanes.has(mutation.record.lane)) invalid(`references missing lane ${mutation.record.lane}`);\n\t\t\t\tif (this.usedIds.has(mutation.record.id)) invalid(`contains duplicate id ${mutation.record.id}`);\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.usedIds.add(mutation.record.id);\n\t\t\t\tthis.records.push(mutation.record);\n\t\t\t\tif (mutation.record.type === \"operation_started\") {\n\t\t\t\t\tlet openOperations = this.openOperationsByLane.get(mutation.record.lane);\n\t\t\t\t\tif (!openOperations) {\n\t\t\t\t\t\topenOperations = new Map();\n\t\t\t\t\t\tthis.openOperationsByLane.set(mutation.record.lane, openOperations);\n\t\t\t\t\t}\n\t\t\t\t\topenOperations.set(mutation.record.id, mutation.record);\n\t\t\t\t} else if (mutation.record.type === \"operation_finished\") {\n\t\t\t\t\tthis.openOperationsByLane.get(mutation.record.lane)?.delete(mutation.record.runId);\n\t\t\t\t}\n\t\t\t\tthis.log.push({ kind: \"record\", seq, record: mutation.record });\n\t\t\t\tif (mutation.record.type === \"usage\") {\n\t\t\t\t\tthis.stats.cachedTokens += mutation.record.usage.cacheRead;\n\t\t\t\t\tthis.stats.uncachedTokens += mutation.record.usage.input + mutation.record.usage.cacheWrite;\n\t\t\t\t\tthis.stats.totalTokens += mutation.record.usage.totalTokens;\n\t\t\t\t\tthis.stats.costTotal += mutation.record.usage.cost.total;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"lane\":\n\t\t\t\tif (mutation.leafId !== null && !this.entriesById.has(mutation.leafId)) {\n\t\t\t\t\tinvalid(`references missing lane target ${mutation.leafId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.lanes.set(mutation.lane, mutation.leafId);\n\t\t\t\tthis.log.push({ kind: \"lane\", seq, lane: mutation.lane, leafId: mutation.leafId });\n\t\t\t\tbreak;\n\t\t\tcase \"fact\":\n\t\t\t\tif (mutation.fact === \"label\" && !this.entriesById.has(mutation.targetId)) {\n\t\t\t\t\tinvalid(`references missing label target ${mutation.targetId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tif (mutation.fact === \"name\") {\n\t\t\t\t\tthis.name = mutation.name;\n\t\t\t\t\tthis.log.push({ kind: \"fact\", seq, fact: \"name\", name: mutation.name });\n\t\t\t\t} else {\n\t\t\t\t\tif (mutation.label === undefined) this.labels.delete(mutation.targetId);\n\t\t\t\t\telse this.labels.set(mutation.targetId, mutation.label);\n\t\t\t\t\tthis.log.push({\n\t\t\t\t\t\tkind: \"fact\",\n\t\t\t\t\t\tseq,\n\t\t\t\t\t\tfact: \"label\",\n\t\t\t\t\t\ttargetId: mutation.targetId,\n\t\t\t\t\t\tlabel: mutation.label,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tgetEntry(id: string): Entry | undefined {\n\t\treturn this.entriesById.get(id);\n\t}\n\n\tfindEntries(query: EntryQuery = {}): Entry[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst results: Entry[] = [];\n\t\tfor (const entry of ordered(this.entries, query.order)) {\n\t\t\tif (!this.matchesEntryQuery(entry, query)) continue;\n\t\t\tresults.push(entry);\n\t\t\tif (results.length === query.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindEntriesOnBranch(query: EntryQuery & BranchBounds & { start: string }): Entry[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst results: Entry[] = [];\n\t\tif (query.order === \"oldestFirst\") {\n\t\t\tfor (const entry of [...this.walkToRoot(query.start)].reverse()) {\n\t\t\t\tconst reachedBound = entry.id === query.stopAtId || entry.type === query.stopAtType;\n\t\t\t\tif (this.matchesEntryQuery(entry, query)) results.push(entry);\n\t\t\t\tif (reachedBound || results.length === query.limit) break;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const entry of this.walkToRoot(query.start, query)) {\n\t\t\t\tif (this.matchesEntryQuery(entry, query)) results.push(entry);\n\t\t\t\tif (results.length === query.limit) break;\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindRecords(query: RecordQuery = {}): LaneRecord[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.afterSeq);\n\t\tconst results: LaneRecord[] = [];\n\t\tfor (const record of ordered(this.records, query.order)) {\n\t\t\tif (!this.matchesRecordQuery(record, query)) continue;\n\t\t\tresults.push(record);\n\t\t\tif (results.length === query.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindOpenOperations(lane: string, options?: { limit?: number }): OperationStartedRecord[] {\n\t\tassertValidLimit(options?.limit);\n\t\tconst openOperationsById = this.openOperationsByLane.get(lane);\n\t\tconst openOperations = openOperationsById ? [...openOperationsById.values()].reverse() : [];\n\t\treturn options?.limit === undefined ? openOperations : openOperations.slice(0, options.limit);\n\t}\n\n\tgetLog(options: LogOptions = {}): LogItem[] {\n\t\tassertValidLimit(options.limit);\n\t\tassertValidCursor(options.afterSeq);\n\t\tconst results: LogItem[] = [];\n\t\tfor (const item of this.log) {\n\t\t\tif (options.afterSeq !== undefined && item.seq <= options.afterSeq) continue;\n\t\t\tresults.push(item);\n\t\t\tif (results.length === options.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tgetName(): string | undefined {\n\t\treturn this.name;\n\t}\n\n\tgetLabel(id: string): string | undefined {\n\t\treturn this.labels.get(id);\n\t}\n\n\tgetStats(): SessionStats {\n\t\treturn this.stats;\n\t}\n\n\tcreateForkMutations(options: ForkOptions): SessionMutation[] {\n\t\tlet copiedEntries: Entry[];\n\t\tlet forkLanes: LanePointer[];\n\t\tif (options.scope === \"tree\") {\n\t\t\tcopiedEntries = this.findEntries({ order: \"oldestFirst\" });\n\t\t\tforkLanes = this.getLanes();\n\t\t} else {\n\t\t\tconst selectedEntryId = options.entryId ?? this.requireLane(\"main\");\n\t\t\tlet targetId: string | null = null;\n\t\t\tif (selectedEntryId !== null) {\n\t\t\t\tconst entry = this.getEntry(selectedEntryId);\n\t\t\t\tif (!entry || entry.type !== \"message\") {\n\t\t\t\t\tthrow new SessionError(\"invalid_fork_target\", `Fork target is not a message entry: ${selectedEntryId}`);\n\t\t\t\t}\n\t\t\t\tconst position = options.position ?? (options.entryId === undefined ? \"at\" : \"before\");\n\t\t\t\ttargetId = position === \"at\" ? entry.id : entry.parentId;\n\t\t\t}\n\t\t\tcopiedEntries = targetId === null ? [] : this.findEntriesOnBranch({ start: targetId, order: \"oldestFirst\" });\n\t\t\tforkLanes = [{ lane: \"main\", leafId: targetId }];\n\t\t}\n\n\t\tconst mutations: SessionMutation[] = [];\n\t\tlet sequence = 1;\n\t\tfor (const sourceEntry of copiedEntries) {\n\t\t\tmutations.push({ kind: \"entry\", entry: { ...structuredClone(sourceEntry), seq: sequence++ } });\n\t\t}\n\t\tfor (const pointer of forkLanes) {\n\t\t\tmutations.push({ kind: \"lane\", seq: sequence++, lane: pointer.lane, leafId: pointer.leafId });\n\t\t}\n\t\tif (this.name !== undefined) {\n\t\t\tmutations.push({ kind: \"fact\", seq: sequence++, fact: \"name\", name: this.name });\n\t\t}\n\t\tfor (const entry of copiedEntries) {\n\t\t\tconst label = this.labels.get(entry.id);\n\t\t\tif (label !== undefined) {\n\t\t\t\tmutations.push({ kind: \"fact\", seq: sequence++, fact: \"label\", targetId: entry.id, label });\n\t\t\t}\n\t\t}\n\t\treturn mutations;\n\t}\n\n\tprivate *walkToRoot(\n\t\tstart: string | null,\n\t\tbounds?: Pick<BranchBounds, \"stopAtId\" | \"stopAtType\">,\n\t): IterableIterator<Entry> {\n\t\tif (start === null) return;\n\t\tconst visited = new Set<string>();\n\t\tlet current = this.entriesById.get(start);\n\t\tif (!current) throw new SessionError(\"not_found\", `Entry not found: ${start}`);\n\t\twhile (current) {\n\t\t\tif (visited.has(current.id)) {\n\t\t\t\tthrow new SessionError(\"invalid_entry\", `Session branch contains a cycle at ${current.id}`);\n\t\t\t}\n\t\t\tvisited.add(current.id);\n\t\t\tyield current;\n\t\t\tif (current.id === bounds?.stopAtId || current.type === bounds?.stopAtType || current.parentId === null) break;\n\t\t\tconst parentId: string = current.parentId;\n\t\t\tcurrent = this.entriesById.get(parentId);\n\t\t\tif (!current) throw new SessionError(\"invalid_entry\", `Entry not found: ${parentId}`);\n\t\t}\n\t}\n\n\tprivate matchesEntryQuery(entry: Entry, query: EntryQuery): boolean {\n\t\treturn (\n\t\t\t(query.type === undefined || entry.type === query.type) &&\n\t\t\t(query.customType === undefined || (entry.type === \"custom\" && entry.customType === query.customType)) &&\n\t\t\t(query.cursor === undefined ||\n\t\t\t\t(query.order === \"oldestFirst\" ? entry.seq > query.cursor.afterSeq : entry.seq < query.cursor.afterSeq))\n\t\t);\n\t}\n\n\tprivate matchesRecordQuery(record: LaneRecord, query: RecordQuery): boolean {\n\t\treturn (\n\t\t\t(query.lane === undefined || record.lane === query.lane) &&\n\t\t\t(query.type === undefined || record.type === query.type) &&\n\t\t\t(query.runId === undefined ||\n\t\t\t\t(record.type === \"operation_started\"\n\t\t\t\t\t? record.id === query.runId\n\t\t\t\t\t: \"runId\" in record && record.runId === query.runId)) &&\n\t\t\t(query.operationKind === undefined ||\n\t\t\t\t(record.type === \"operation_started\" && record.intent.kind === query.operationKind)) &&\n\t\t\t(query.afterSeq === undefined || record.seq > query.afterSeq)\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/harness/session/state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype BranchBounds,\n\ttype Entry,\n\ttype EntryOrder,\n\ttype EntryQuery,\n\ttype ForkOptions,\n\ttype LanePointer,\n\ttype LaneRecord,\n\ttype LogItem,\n\ttype LogOptions,\n\ttype OperationStartedRecord,\n\ttype RecordQuery,\n\tSessionError,\n\ttype SessionStats,\n} from \"./types.ts\";\n\nexport type SessionMutation =\n\t| { kind: \"entry\"; lane?: string; entry: Entry }\n\t| { kind: \"record\"; record: LaneRecord }\n\t| { kind: \"lane\"; seq: number; lane: string; leafId: string | null }\n\t| { kind: \"fact\"; seq: number; fact: \"name\"; name: string | undefined }\n\t| { kind: \"fact\"; seq: number; fact: \"label\"; targetId: string; label: string | undefined };\n\ntype InvalidMutation = (message: string) => never;\n\nfunction invalidMutation(message: string): never {\n\tthrow new SessionError(\"invalid_entry\", `Invalid session mutation: ${message}`);\n}\n\nfunction assertValidLimit(limit: number | undefined): void {\n\tif (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"limit must be a positive integer\");\n\t}\n}\n\nfunction assertValidCursor(afterSeq: number | undefined): void {\n\tif (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"cursor sequence must be a non-negative integer\");\n\t}\n}\n\nfunction* ordered<T>(items: readonly T[], order: EntryOrder | undefined): IterableIterator<T> {\n\tif (order === \"oldestFirst\") {\n\t\tyield* items;\n\t\treturn;\n\t}\n\tfor (let index = items.length - 1; index >= 0; index--) yield items[index]!;\n}\n\nexport class SessionState {\n\tprivate sequence = 0;\n\tprivate readonly usedIds = new Set<string>();\n\tprivate readonly entries: Entry[] = [];\n\tprivate readonly entriesById = new Map<string, Entry>();\n\tprivate readonly records: LaneRecord[] = [];\n\tprivate readonly openOperationsByLane = new Map<string, Map<string, OperationStartedRecord>>();\n\tprivate readonly lanes = new Map<string, string | null>([[\"main\", null]]);\n\tprivate readonly log: LogItem[] = [];\n\tprivate readonly stats: SessionStats = {\n\t\tmessageCount: 0,\n\t\tcachedTokens: 0,\n\t\tuncachedTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcostTotal: 0,\n\t};\n\tprivate name: string | undefined;\n\tprivate readonly labels = new Map<string, string>();\n\n\tget nextSequence(): number {\n\t\treturn this.sequence + 1;\n\t}\n\n\tgetLanes(): LanePointer[] {\n\t\treturn [...this.lanes].map(([lane, leafId]) => ({ lane, leafId }));\n\t}\n\n\trequireLane(lane: string): string | null {\n\t\tconst leafId = this.lanes.get(lane);\n\t\tif (leafId === undefined) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\t\treturn leafId;\n\t}\n\n\tvalidateNewLane(lane: string): void {\n\t\tif (this.lanes.has(lane)) throw new SessionError(\"already_exists\", `Lane already exists: ${lane}`);\n\t}\n\n\tvalidateTarget(targetId: string | null): void {\n\t\tif (targetId !== null && !this.entriesById.has(targetId)) {\n\t\t\tthrow new SessionError(\"not_found\", `Entry not found: ${targetId}`);\n\t\t}\n\t}\n\n\tvalidateUnusedId(id: string): void {\n\t\tif (this.usedIds.has(id)) throw new SessionError(\"already_exists\", `Session id already exists: ${id}`);\n\t}\n\n\tapplyMutation(mutation: SessionMutation, invalid: InvalidMutation = invalidMutation): void {\n\t\tconst seq =\n\t\t\tmutation.kind === \"entry\"\n\t\t\t\t? mutation.entry.seq\n\t\t\t\t: mutation.kind === \"record\"\n\t\t\t\t\t? mutation.record.seq\n\t\t\t\t\t: mutation.seq;\n\t\tif (seq !== this.sequence + 1) invalid(`has non-consecutive seq ${seq}`);\n\n\t\tswitch (mutation.kind) {\n\t\t\tcase \"entry\": {\n\t\t\t\tif (this.usedIds.has(mutation.entry.id)) invalid(`contains duplicate id ${mutation.entry.id}`);\n\t\t\t\tif (mutation.lane !== undefined) {\n\t\t\t\t\tconst leafId = this.lanes.get(mutation.lane);\n\t\t\t\t\tif (leafId === undefined) invalid(`references missing lane ${mutation.lane}`);\n\t\t\t\t\tif (mutation.entry.parentId !== leafId) invalid(\"does not chain to the lane leaf\");\n\t\t\t\t}\n\t\t\t\tif (mutation.entry.parentId !== null && !this.entriesById.has(mutation.entry.parentId)) {\n\t\t\t\t\tinvalid(`references missing parent ${mutation.entry.parentId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.usedIds.add(mutation.entry.id);\n\t\t\t\tthis.entries.push(mutation.entry);\n\t\t\t\tthis.entriesById.set(mutation.entry.id, mutation.entry);\n\t\t\t\tif (mutation.lane !== undefined) this.lanes.set(mutation.lane, mutation.entry.id);\n\t\t\t\tthis.log.push({ kind: \"entry\", seq, entry: mutation.entry });\n\t\t\t\tif (mutation.entry.type === \"message\") this.stats.messageCount += 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"record\": {\n\t\t\t\tif (!this.lanes.has(mutation.record.lane)) invalid(`references missing lane ${mutation.record.lane}`);\n\t\t\t\tif (this.usedIds.has(mutation.record.id)) invalid(`contains duplicate id ${mutation.record.id}`);\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.usedIds.add(mutation.record.id);\n\t\t\t\tthis.records.push(mutation.record);\n\t\t\t\tif (mutation.record.type === \"operation_started\") {\n\t\t\t\t\tlet openOperations = this.openOperationsByLane.get(mutation.record.lane);\n\t\t\t\t\tif (!openOperations) {\n\t\t\t\t\t\topenOperations = new Map();\n\t\t\t\t\t\tthis.openOperationsByLane.set(mutation.record.lane, openOperations);\n\t\t\t\t\t}\n\t\t\t\t\topenOperations.set(mutation.record.id, mutation.record);\n\t\t\t\t} else if (mutation.record.type === \"operation_finished\") {\n\t\t\t\t\tthis.openOperationsByLane.get(mutation.record.lane)?.delete(mutation.record.runId);\n\t\t\t\t}\n\t\t\t\tthis.log.push({ kind: \"record\", seq, record: mutation.record });\n\t\t\t\tif (mutation.record.type === \"usage\") {\n\t\t\t\t\tthis.stats.cachedTokens += mutation.record.usage.cacheRead;\n\t\t\t\t\tthis.stats.uncachedTokens += mutation.record.usage.input + mutation.record.usage.cacheWrite;\n\t\t\t\t\tthis.stats.totalTokens += mutation.record.usage.totalTokens;\n\t\t\t\t\tthis.stats.costTotal += mutation.record.usage.cost.total;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"lane\":\n\t\t\t\tif (mutation.leafId !== null && !this.entriesById.has(mutation.leafId)) {\n\t\t\t\t\tinvalid(`references missing lane target ${mutation.leafId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tthis.lanes.set(mutation.lane, mutation.leafId);\n\t\t\t\tthis.log.push({ kind: \"lane\", seq, lane: mutation.lane, leafId: mutation.leafId });\n\t\t\t\tbreak;\n\t\t\tcase \"fact\":\n\t\t\t\tif (mutation.fact === \"label\" && !this.entriesById.has(mutation.targetId)) {\n\t\t\t\t\tinvalid(`references missing label target ${mutation.targetId}`);\n\t\t\t\t}\n\t\t\t\tthis.sequence = seq;\n\t\t\t\tif (mutation.fact === \"name\") {\n\t\t\t\t\tthis.name = mutation.name;\n\t\t\t\t\tthis.log.push({ kind: \"fact\", seq, fact: \"name\", name: mutation.name });\n\t\t\t\t} else {\n\t\t\t\t\tif (mutation.label === undefined) this.labels.delete(mutation.targetId);\n\t\t\t\t\telse this.labels.set(mutation.targetId, mutation.label);\n\t\t\t\t\tthis.log.push({\n\t\t\t\t\t\tkind: \"fact\",\n\t\t\t\t\t\tseq,\n\t\t\t\t\t\tfact: \"label\",\n\t\t\t\t\t\ttargetId: mutation.targetId,\n\t\t\t\t\t\tlabel: mutation.label,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tgetEntry(id: string): Entry | undefined {\n\t\treturn this.entriesById.get(id);\n\t}\n\n\tfindEntries(query: EntryQuery = {}): Entry[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst results: Entry[] = [];\n\t\tfor (const entry of ordered(this.entries, query.order)) {\n\t\t\tif (!this.matchesEntryQuery(entry, query)) continue;\n\t\t\tresults.push(entry);\n\t\t\tif (results.length === query.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindEntriesOnBranch(query: EntryQuery & BranchBounds & { start: string }): Entry[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst results: Entry[] = [];\n\t\tif (query.order === \"oldestFirst\") {\n\t\t\tfor (const entry of [...this.walkToRoot(query.start)].reverse()) {\n\t\t\t\tconst reachedBound = entry.id === query.stopAtId || entry.type === query.stopAtType;\n\t\t\t\tif (this.matchesEntryQuery(entry, query)) results.push(entry);\n\t\t\t\tif (reachedBound || results.length === query.limit) break;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const entry of this.walkToRoot(query.start, query)) {\n\t\t\t\tif (this.matchesEntryQuery(entry, query)) results.push(entry);\n\t\t\t\tif (results.length === query.limit) break;\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindRecords(query: RecordQuery = {}): LaneRecord[] {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.afterSeq);\n\t\tconst results: LaneRecord[] = [];\n\t\tfor (const record of ordered(this.records, query.order)) {\n\t\t\tif (!this.matchesRecordQuery(record, query)) continue;\n\t\t\tresults.push(record);\n\t\t\tif (results.length === query.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tfindOpenOperations(lane: string, options?: { limit?: number }): OperationStartedRecord[] {\n\t\tassertValidLimit(options?.limit);\n\t\tconst openOperationsById = this.openOperationsByLane.get(lane);\n\t\tconst openOperations = openOperationsById ? [...openOperationsById.values()].reverse() : [];\n\t\treturn options?.limit === undefined ? openOperations : openOperations.slice(0, options.limit);\n\t}\n\n\tgetLog(options: LogOptions = {}): LogItem[] {\n\t\tassertValidLimit(options.limit);\n\t\tassertValidCursor(options.afterSeq);\n\t\tconst results: LogItem[] = [];\n\t\tfor (const item of this.log) {\n\t\t\tif (options.afterSeq !== undefined && item.seq <= options.afterSeq) continue;\n\t\t\tresults.push(item);\n\t\t\tif (results.length === options.limit) break;\n\t\t}\n\t\treturn results;\n\t}\n\n\tgetName(): string | undefined {\n\t\treturn this.name;\n\t}\n\n\tgetLabel(id: string): string | undefined {\n\t\treturn this.labels.get(id);\n\t}\n\n\tgetStats(): SessionStats {\n\t\treturn this.stats;\n\t}\n\n\tcreateForkMutations(options: ForkOptions): SessionMutation[] {\n\t\tlet copiedEntries: Entry[];\n\t\tlet forkLanes: LanePointer[];\n\t\tif (options.scope === \"tree\") {\n\t\t\tcopiedEntries = this.findEntries({ order: \"oldestFirst\" });\n\t\t\tforkLanes = this.getLanes();\n\t\t} else {\n\t\t\tconst selectedEntryId = options.entryId ?? this.requireLane(\"main\");\n\t\t\tlet targetId: string | null = null;\n\t\t\tif (selectedEntryId !== null) {\n\t\t\t\tconst entry = this.getEntry(selectedEntryId);\n\t\t\t\tif (!entry || entry.type !== \"message\") {\n\t\t\t\t\tthrow new SessionError(\"invalid_fork_target\", `Fork target is not a message entry: ${selectedEntryId}`);\n\t\t\t\t}\n\t\t\t\tconst position = options.position ?? (options.entryId === undefined ? \"at\" : \"before\");\n\t\t\t\ttargetId = position === \"at\" ? entry.id : entry.parentId;\n\t\t\t}\n\t\t\tcopiedEntries = targetId === null ? [] : this.findEntriesOnBranch({ start: targetId, order: \"oldestFirst\" });\n\t\t\tforkLanes = [{ lane: \"main\", leafId: targetId }];\n\t\t}\n\n\t\tconst mutations: SessionMutation[] = [];\n\t\tlet sequence = 1;\n\t\tfor (const sourceEntry of copiedEntries) {\n\t\t\tmutations.push({ kind: \"entry\", entry: { ...structuredClone(sourceEntry), seq: sequence++ } });\n\t\t}\n\t\tfor (const pointer of forkLanes) {\n\t\t\tmutations.push({ kind: \"lane\", seq: sequence++, lane: pointer.lane, leafId: pointer.leafId });\n\t\t}\n\t\tif (this.name !== undefined) {\n\t\t\tmutations.push({ kind: \"fact\", seq: sequence++, fact: \"name\", name: this.name });\n\t\t}\n\t\tfor (const entry of copiedEntries) {\n\t\t\tconst label = this.labels.get(entry.id);\n\t\t\tif (label !== undefined) {\n\t\t\t\tmutations.push({ kind: \"fact\", seq: sequence++, fact: \"label\", targetId: entry.id, label });\n\t\t\t}\n\t\t}\n\t\treturn mutations;\n\t}\n\n\tprivate *walkToRoot(\n\t\tstart: string | null,\n\t\tbounds?: Pick<BranchBounds, \"stopAtId\" | \"stopAtType\">,\n\t): IterableIterator<Entry> {\n\t\tif (start === null) return;\n\t\tconst visited = new Set<string>();\n\t\tlet current = this.entriesById.get(start);\n\t\tif (!current) throw new SessionError(\"not_found\", `Entry not found: ${start}`);\n\t\twhile (current) {\n\t\t\tif (visited.has(current.id)) {\n\t\t\t\tthrow new SessionError(\"invalid_entry\", `Session branch contains a cycle at ${current.id}`);\n\t\t\t}\n\t\t\tvisited.add(current.id);\n\t\t\tyield current;\n\t\t\tif (current.id === bounds?.stopAtId || current.type === bounds?.stopAtType || current.parentId === null) break;\n\t\t\tconst parentId: string = current.parentId;\n\t\t\tcurrent = this.entriesById.get(parentId);\n\t\t\tif (!current) throw new SessionError(\"invalid_entry\", `Entry not found: ${parentId}`);\n\t\t}\n\t}\n\n\tprivate matchesEntryQuery(entry: Entry, query: EntryQuery): boolean {\n\t\treturn (\n\t\t\t(query.type === undefined || entry.type === query.type) &&\n\t\t\t(query.customType === undefined || (entry.type === \"custom\" && entry.customType === query.customType)) &&\n\t\t\t(query.cursor === undefined ||\n\t\t\t\t(query.order === \"oldestFirst\" ? entry.seq > query.cursor.afterSeq : entry.seq < query.cursor.afterSeq))\n\t\t);\n\t}\n\n\tprivate matchesRecordQuery(record: LaneRecord, query: RecordQuery): boolean {\n\t\treturn (\n\t\t\t(query.lane === undefined || record.lane === query.lane) &&\n\t\t\t(query.type === undefined || record.type === query.type) &&\n\t\t\t(query.runId === undefined ||\n\t\t\t\t(record.type === \"operation_started\"\n\t\t\t\t\t? record.id === query.runId\n\t\t\t\t\t: \"runId\" in record && record.runId === query.runId)) &&\n\t\t\t(query.operationKind === undefined ||\n\t\t\t\t(record.type === \"operation_started\" && record.intent.kind === query.operationKind)) &&\n\t\t\t(query.afterSeq === undefined || record.seq > query.afterSeq)\n\t\t);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/testing/conformance.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AA+E9F,qGAAqG;AACrG,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,4BAA4B,GACnC,SAAS,6BAA6B,EAAE,CAm4B1C","sourcesContent":["import { deepStrictEqual, ok, rejects, strictEqual } from \"node:assert/strict\";\nimport type { AgentMessage } from \"../../../types.ts\";\nimport type {\n\tCustomEntry,\n\tEntry,\n\tMessageEntry,\n\tNewRecord,\n\tOperationStartedRecord,\n\tSessionErrorCode,\n\tSessionRepo,\n} from \"../types.ts\";\nimport type { SessionBackendConformanceCase, SessionBackendFixtureFactory } from \"./types.ts\";\n\nfunction createUserMessage(text: string): AgentMessage {\n\treturn {\n\t\trole: \"user\",\n\t\tcontent: [{ type: \"text\", text }],\n\t\ttimestamp: 1,\n\t};\n}\n\nfunction createAssistantMessage(text: string): AgentMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [{ type: \"text\", text }],\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"anthropic\",\n\t\tmodel: \"claude-sonnet-4-5\",\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: 1,\n\t};\n}\n\nfunction operationStarted(\n\tid: string,\n\t{ lane, kind }: { lane: string; kind: OperationStartedRecord[\"intent\"][\"kind\"] },\n): NewRecord<OperationStartedRecord> {\n\tlet intent: OperationStartedRecord[\"intent\"];\n\tswitch (kind) {\n\t\tcase \"run\":\n\t\t\tintent = { kind, originalPrompt: [], initialMessages: [] };\n\t\t\tbreak;\n\t\tcase \"compaction\":\n\t\t\tintent = { kind, resultEntryId: `${id}-result` };\n\t\t\tbreak;\n\t\tcase \"navigation\":\n\t\t\tintent = { kind, targetId: null, summarize: false };\n\t\t\tbreak;\n\t}\n\treturn { type: \"operation_started\", id, lane, sourceLeafId: null, intent };\n}\n\nasync function entryIds(entries: Promise<Entry[]>): Promise<string[]> {\n\treturn (await entries).map((entry) => entry.id);\n}\n\nasync function rejectsWithCode(operation: Promise<unknown>, code: SessionErrorCode): Promise<void> {\n\tawait rejects(\n\t\toperation,\n\t\t(error: unknown) => typeof error === \"object\" && error !== null && \"code\" in error && error.code === code,\n\t\t`Expected SessionError with code ${code}`,\n\t);\n}\n\ntype ConformanceTest = (repository: SessionRepo) => Promise<void>;\n\nfunction createCase(\n\tfactory: SessionBackendFixtureFactory,\n\tgroup: string,\n\tname: string,\n\ttest: ConformanceTest,\n): SessionBackendConformanceCase {\n\treturn {\n\t\tgroup,\n\t\tname,\n\t\tasync run() {\n\t\t\tawait using fixture = await factory();\n\t\t\tawait test(fixture.repository);\n\t\t},\n\t};\n}\n\n/** Creates the session backend conformance cases. Each case creates and disposes its own fixture. */\nexport function createSessionBackendConformance(\n\tfactory: SessionBackendFixtureFactory,\n): readonly SessionBackendConformanceCase[] {\n\treturn [\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"entries and lanes\",\n\t\t\t\"assigns parents and one sequence across every mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst root = await session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.createLane(\"thread\", root.id);\n\t\t\t\tconst child = await session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"child\", customType: \"note\", data: { value: 1 } },\n\t\t\t\t\t\"thread\",\n\t\t\t\t);\n\t\t\t\tconst record = await session.appendRecord(operationStarted(\"run\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.setName(\"Example\");\n\t\t\t\tawait session.setLabel(root.id, \"checkpoint\");\n\t\t\t\tawait session.moveLane(\"main\", child.id);\n\n\t\t\t\tdeepStrictEqual({ parentId: root.parentId, seq: root.seq }, { parentId: null, seq: 1 });\n\t\t\t\tdeepStrictEqual({ parentId: child.parentId, seq: child.seq }, { parentId: \"root\", seq: 3 });\n\t\t\t\tstrictEqual(record.seq, 4);\n\t\t\t\tfor (const timestamp of [root.timestamp, child.timestamp, record.timestamp]) {\n\t\t\t\t\tok(\n\t\t\t\t\t\tNumber.isSafeInteger(timestamp) && timestamp >= 0,\n\t\t\t\t\t\t\"storage-assigned timestamps must be Unix milliseconds\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).map((item) => [item.kind, item.seq]),\n\t\t\t\t\t[\n\t\t\t\t\t\t[\"entry\", 1],\n\t\t\t\t\t\t[\"lane\", 2],\n\t\t\t\t\t\t[\"entry\", 3],\n\t\t\t\t\t\t[\"record\", 4],\n\t\t\t\t\t\t[\"fact\", 5],\n\t\t\t\t\t\t[\"fact\", 6],\n\t\t\t\t\t\t[\"lane\", 7],\n\t\t\t\t\t],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [\n\t\t\t\t\t{ lane: \"main\", leafId: \"child\" },\n\t\t\t\t\t{ lane: \"thread\", leafId: \"child\" },\n\t\t\t\t]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"commits records and lane moves as separate mutations\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst root = await session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tconst finished = await session.appendRecord({\n\t\t\t\t\ttype: \"operation_finished\",\n\t\t\t\t\tid: \"finish\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\toutcome: \"completed\",\n\t\t\t\t});\n\n\t\t\t\tstrictEqual(finished.seq, 2);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [{ lane: \"main\", leafId: \"root\" }]);\n\t\t\t\tawait session.moveLane(\"main\", null);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [{ lane: \"main\", leafId: null }]);\n\t\t\t\tdeepStrictEqual(await session.getLog(), [\n\t\t\t\t\t{ kind: \"entry\", seq: 1, entry: root },\n\t\t\t\t\t{ kind: \"record\", seq: 2, record: finished },\n\t\t\t\t\t{ kind: \"lane\", seq: 3, lane: \"main\", leafId: null },\n\t\t\t\t]);\n\n\t\t\t\tawait rejectsWithCode(session.moveLane(\"main\", \"missing\"), \"not_found\");\n\t\t\t\tstrictEqual((await session.findRecords()).length, 1);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).map((item) => item.seq),\n\t\t\t\t\t[1, 2, 3],\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"rejects duplicate ids without changing state\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"shared\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendRecord(operationStarted(\"shared\", { lane: \"main\", kind: \"run\" })),\n\t\t\t\t\"already_exists\",\n\t\t\t);\n\t\t\tawait session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"run\", customType: \"note\" }, \"main\"),\n\t\t\t\t\"already_exists\",\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await session.getLog()).map((item) => item.seq),\n\t\t\t\t[1, 2],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"isolates lanes while sharing the tree\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.createLane(\"thread\", \"root\");\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"main-child\", message: createUserMessage(\"main\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"thread-child\", message: createUserMessage(\"thread\") },\n\t\t\t\t\"thread\",\n\t\t\t);\n\n\t\t\tdeepStrictEqual(await session.getLanes(), [\n\t\t\t\t{ lane: \"main\", leafId: \"main-child\" },\n\t\t\t\t{ lane: \"thread\", leafId: \"thread-child\" },\n\t\t\t]);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ start: \"main-child\", order: \"oldestFirst\" })), [\n\t\t\t\t\"root\",\n\t\t\t\t\"main-child\",\n\t\t\t]);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ start: \"thread-child\", order: \"oldestFirst\" })), [\n\t\t\t\t\"root\",\n\t\t\t\t\"thread-child\",\n\t\t\t]);\n\t\t}),\n\n\t\tcreateCase(factory, \"queries and facts\", \"rejects invalid queries before empty reads\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"invalid-queries\" });\n\t\t\tawait session.createLane(\"thread\", null);\n\t\t\tconst thread = session.view(\"thread\");\n\n\t\t\tawait rejectsWithCode(session.findEntries({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findEntry({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findEntriesOnBranch({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(thread.findEntriesOnBranch({ cursor: { afterSeq: -1 } }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(thread.findEntryOnBranch({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ operationKind: \"run\" }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ type: \"step_attempt\", operationKind: \"run\" }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findOpenOperations(\"main\", { limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findOpenOperations(\"main\", { limit: -1 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.getLog({ afterSeq: -1 }), \"invalid_query\");\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"queries and facts\",\n\t\t\t\"supports bounded filtered and cursor-based queries\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"old-note\", customType: \"note\", data: 1 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry(\n\t\t\t\t\t{ type: \"compaction\", id: \"compact\", summary: \"summary\", retainedTail: [], tokensBefore: 10 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"new-note\", customType: \"note\", data: 2 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"tail\", message: createAssistantMessage(\"tail\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\n\t\t\t\tdeepStrictEqual(await entryIds(session.findEntries()), [\"tail\", \"new-note\", \"compact\", \"old-note\", \"root\"]);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntries({ order: \"oldestFirst\", cursor: { afterSeq: 2 }, limit: 2 })),\n\t\t\t\t\t[\"compact\", \"new-note\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(await entryIds(session.findEntries({ customType: \"note\" })), [\"new-note\", \"old-note\"]);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntriesOnBranch({ start: \"tail\", customType: \"note\", limit: 1 })),\n\t\t\t\t\t[\"new-note\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(\n\t\t\t\t\t\tsession.findEntriesOnBranch({ start: \"tail\", stopAtType: \"compaction\", type: \"message\" }),\n\t\t\t\t\t),\n\t\t\t\t\t[\"tail\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntriesOnBranch({ start: \"tail\", stopAtId: \"tail\", type: \"custom\" })),\n\t\t\t\t\t[],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(\n\t\t\t\t\t\tsession.findEntriesOnBranch({ start: \"tail\", stopAtType: \"custom\", order: \"oldestFirst\" }),\n\t\t\t\t\t),\n\t\t\t\t\t[\"root\", \"old-note\"],\n\t\t\t\t);\n\t\t\t\tawait rejectsWithCode(session.findEntries({ limit: 0 }), \"invalid_query\");\n\t\t\t\tawait rejectsWithCode(session.findEntriesOnBranch({ start: \"missing\" }), \"not_found\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"keeps lane names permanent with their recovery records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.createLane(\"thread\", null);\n\t\t\t\tawait session.appendRecord(operationStarted(\"old-run\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"queue_enqueued\",\n\t\t\t\t\tid: \"old-next-run\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\tqueue: \"nextRun\",\n\t\t\t\t\ttarget: { type: \"message\", id: \"queued-message\", message: createUserMessage(\"queued\") },\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ lane: \"thread\" })).map((record) => record.id),\n\t\t\t\t\t[\"old-next-run\", \"old-run\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).flatMap((item) => (item.kind === \"record\" ? [item.record.id] : [])),\n\t\t\t\t\t[\"old-run\", \"old-next-run\"],\n\t\t\t\t);\n\t\t\t\tawait rejectsWithCode(session.createLane(\"thread\", null), \"already_exists\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"persists queue cancellation without consuming its target\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst enqueued = await session.appendRecord({\n\t\t\t\t\ttype: \"queue_enqueued\",\n\t\t\t\t\tid: \"enqueue\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tqueue: \"nextRun\",\n\t\t\t\t\ttarget: { type: \"message\", id: \"queued-message\", message: createUserMessage(\"queued\") },\n\t\t\t\t});\n\t\t\t\tconst cancelled = await session.appendRecord({\n\t\t\t\t\ttype: \"queue_cancelled\",\n\t\t\t\t\tid: \"cancel\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tentryId: \"queued-message\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual({ seq: cancelled.seq, entryId: cancelled.entryId }, { seq: 2, entryId: \"queued-message\" });\n\t\t\t\tstrictEqual(\"runId\" in cancelled, false);\n\t\t\t\tstrictEqual(await session.getEntry(\"queued-message\"), undefined);\n\t\t\t\tconst cancellations = await session.findRecords({ type: \"queue_cancelled\" });\n\t\t\t\tstrictEqual(cancellations[0]?.entryId, \"queued-message\");\n\t\t\t\tdeepStrictEqual(cancellations, [cancelled]);\n\t\t\t\tdeepStrictEqual(await session.getLog(), [\n\t\t\t\t\t{ kind: \"record\", seq: enqueued.seq, record: enqueued },\n\t\t\t\t\t{ kind: \"record\", seq: cancelled.seq, record: cancelled },\n\t\t\t\t]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"filters records by lane type run sequence and order\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendRecord(operationStarted(\"run-1\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"step_attempt\",\n\t\t\t\t\tid: \"attempt-1\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run-1\",\n\t\t\t\t\tstep: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tresultEntryId: \"assistant-1\",\n\t\t\t\t});\n\t\t\t\tawait session.createLane(\"thread\", null);\n\t\t\t\tawait session.appendRecord(operationStarted(\"run-2\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"step_attempt\",\n\t\t\t\t\tid: \"attempt-2\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\trunId: \"run-2\",\n\t\t\t\t\tstep: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tresultEntryId: \"assistant-2\",\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ lane: \"thread\" })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-2\", \"run-2\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ type: \"step_attempt\", order: \"oldestFirst\" })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-1\", \"attempt-2\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ runId: \"run-1\", afterSeq: 1 })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-1\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ limit: 1 })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-2\"],\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"records and log\", \"filters operation starts by operation kind\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendRecord(operationStarted(\"run-old\", { lane: \"main\", kind: \"run\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"run-old-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"run-old\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"compaction\", { lane: \"main\", kind: \"compaction\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"compaction-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"compaction\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"navigation\", { lane: \"main\", kind: \"navigation\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"navigation-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"navigation\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"run-new\", { lane: \"main\", kind: \"run\" }));\n\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"run\",\n\t\t\t\t\t\torder: \"oldestFirst\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"run-old\", \"run-new\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"compaction\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"compaction\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"navigation\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"navigation\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"run\",\n\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"run-new\"],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"records and log\", \"tracks and enforces one open operation per lane\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), []);\n\n\t\t\tconst first = await session.appendRecord(operationStarted(\"first\", { lane: \"main\", kind: \"run\" }));\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [first]);\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendRecord(operationStarted(\"second\", { lane: \"main\", kind: \"run\" })),\n\t\t\t\t\"storage\",\n\t\t\t);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [first]);\n\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"finish-first\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: first.id,\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), []);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"does not let an earlier finish close a later start\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"operation_finished\",\n\t\t\t\t\tid: \"finish-before-start\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\toutcome: \"completed\",\n\t\t\t\t});\n\t\t\t\tconst started = await session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [started]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"records and log\", \"scopes open operations by lane and limit\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.createLane(\"thread\", null);\n\t\t\tconst mainRun = await session.appendRecord(operationStarted(\"main-run\", { lane: \"main\", kind: \"run\" }));\n\t\t\tconst threadNavigation = await session.appendRecord(\n\t\t\t\toperationStarted(\"thread-navigation\", { lane: \"thread\", kind: \"navigation\" }),\n\t\t\t);\n\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\"), [mainRun]);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 1 }), [mainRun]);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"thread\", { limit: 2 }), [threadNavigation]);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"returns immutable open-operation records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst committed = await session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tconst [read] = await session.findOpenOperations(\"main\");\n\t\t\t\tif (read?.intent.kind !== \"run\") throw new Error(\"Expected an open run operation\");\n\t\t\t\tread.intent.originalPrompt.push(createUserMessage(\"mutated\"));\n\n\t\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\"), [committed]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"queries and facts\",\n\t\t\t\"keeps latest-value facts and computes ledger statistics across lanes\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst assistant = createAssistantMessage(\"answer\");\n\t\t\t\tif (assistant.role !== \"assistant\") throw new Error(\"Expected assistant message\");\n\t\t\t\tassistant.usage = {\n\t\t\t\t\tinput: 10,\n\t\t\t\t\toutput: 5,\n\t\t\t\t\tcacheRead: 3,\n\t\t\t\t\tcacheWrite: 2,\n\t\t\t\t\ttotalTokens: 20,\n\t\t\t\t\tcost: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },\n\t\t\t\t};\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"user\", message: createUserMessage(\"question\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<MessageEntry>({ type: \"message\", id: \"assistant\", message: assistant }, \"main\");\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"assistant-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"assistant\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\tentryId: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tstopReason: \"stop\",\n\t\t\t\t\tusage: assistant.usage,\n\t\t\t\t});\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"deferred-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"deferred_fetch\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\tentryId: \"deferred-result\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tstopReason: \"deferred\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tawait session.createLane(\"thread\", \"assistant\");\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"correction\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\tcause: \"adjustment\",\n\t\t\t\t\tdetails: { reason: \"provider correction\" },\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: -2,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: -2,\n\t\t\t\t\t\tcost: { input: -0.5, output: 0, cacheRead: 0, cacheWrite: 0, total: -0.5 },\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tawait session.setName(\"First\");\n\t\t\t\tawait session.setName(\"Second\");\n\t\t\t\tawait session.setLabel(\"user\", \"keep\");\n\t\t\t\tawait session.setLabel(\"user\", undefined);\n\t\t\t\tawait rejectsWithCode(session.setLabel(\"missing\", \"checkpoint\"), \"not_found\");\n\n\t\t\t\tstrictEqual(await session.getName(), \"Second\");\n\t\t\t\tstrictEqual(await session.getLabel(\"user\"), undefined);\n\t\t\t\tconst usageRecords = await session.findRecords({ type: \"usage\", order: \"oldestFirst\" });\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tusageRecords.map((record) => record.cause),\n\t\t\t\t\t[\"assistant\", \"deferred_fetch\", \"adjustment\"],\n\t\t\t\t);\n\t\t\t\tconst deferredUsage = usageRecords.find((record) => record.cause === \"deferred_fetch\");\n\t\t\t\tif (deferredUsage?.cause !== \"deferred_fetch\") throw new Error(\"Expected deferred usage record\");\n\t\t\t\tstrictEqual(deferredUsage.stopReason, \"deferred\");\n\t\t\t\tdeepStrictEqual(await session.getStats(), {\n\t\t\t\t\tmessageCount: 2,\n\t\t\t\t\tcachedTokens: 3,\n\t\t\t\t\tuncachedTokens: 10,\n\t\t\t\t\ttotalTokens: 18,\n\t\t\t\t\tcostTotal: 9.5,\n\t\t\t\t});\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"validation and immutability\", \"returns immutable copies from reads\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"immutable\" });\n\t\t\tconst metadata = await session.getMetadata();\n\t\t\tconst data = { nested: { value: 1 } };\n\t\t\tawait session.appendEntry<CustomEntry>({ type: \"custom\", id: \"custom\", customType: \"note\", data }, \"main\");\n\t\t\tdata.nested.value = 50;\n\t\t\tconst read = await session.getEntry(\"custom\");\n\t\t\tif (read?.type !== \"custom\") throw new Error(\"Expected custom entry\");\n\t\t\t(read.data as { nested: { value: number } }).nested.value = 99;\n\t\t\tconst readMetadata = await session.getMetadata();\n\t\t\treadMetadata.id = \"changed\";\n\t\t\tconst log = await session.getLog();\n\t\t\tif (log[0]?.kind !== \"entry\" || log[0].entry.type !== \"custom\") throw new Error(\"Expected entry log\");\n\t\t\t(log[0].entry.data as { nested: { value: number } }).nested.value = 100;\n\n\t\t\tdeepStrictEqual(await session.getMetadata(), metadata);\n\t\t\tdeepStrictEqual(await session.getEntry(\"custom\"), {\n\t\t\t\ttype: \"custom\",\n\t\t\t\tid: \"custom\",\n\t\t\t\tcustomType: \"note\",\n\t\t\t\tdata: { nested: { value: 1 } },\n\t\t\t\tparentId: null,\n\t\t\t\tseq: 1,\n\t\t\t\ttimestamp: read.timestamp,\n\t\t\t});\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"validates lane lifecycle and targets\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait rejectsWithCode(session.createLane(\"main\", null), \"already_exists\");\n\t\t\tawait rejectsWithCode(session.createLane(\"thread\", \"missing\"), \"not_found\");\n\t\t\tawait rejectsWithCode(session.moveLane(\"missing\", null), \"invalid_lane\");\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"binds lane views without caching leaves\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tconst root = await session.appendMessage(createUserMessage(\"root\"));\n\t\t\tawait session.createLane(\"thread\", root);\n\t\t\tconst thread = session.view(\"thread\");\n\t\t\tconst [mainChild, threadChild] = await Promise.all([\n\t\t\t\tsession.appendMessage(createUserMessage(\"main\")),\n\t\t\t\tthread.appendMessage(createUserMessage(\"thread\")),\n\t\t\t]);\n\n\t\t\tstrictEqual(await session.getLeafId(), mainChild);\n\t\t\tstrictEqual(await thread.getLeafId(), threadChild);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ order: \"oldestFirst\" })), [root, mainChild]);\n\t\t\tdeepStrictEqual(await entryIds(thread.findEntriesOnBranch({ order: \"oldestFirst\" })), [root, threadChild]);\n\t\t\tconst empty = await repository.create({ id: \"empty\" });\n\t\t\tdeepStrictEqual(await empty.findEntriesOnBranch(), []);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"entries and lanes\",\n\t\t\t\"appends provisioned entries with their existing ids\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst entry = await session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"provisioned\", customType: \"note\", data: { value: 1 } },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\n\t\t\t\tstrictEqual(entry.customType, \"note\");\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t{ id: entry.id, parentId: entry.parentId, seq: entry.seq },\n\t\t\t\t\t{ id: \"provisioned\", parentId: null, seq: 1 },\n\t\t\t\t);\n\t\t\t\tstrictEqual(await session.getLeafId(), \"provisioned\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"persists tool-result termination decisions\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tconst entry = await session.appendEntry<MessageEntry>(\n\t\t\t\t{\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t\tid: \"tool-result\",\n\t\t\t\t\tmessage: {\n\t\t\t\t\t\trole: \"toolResult\",\n\t\t\t\t\t\ttoolCallId: \"call-1\",\n\t\t\t\t\t\ttoolName: \"example\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"done\" }],\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t\ttimestamp: 1,\n\t\t\t\t\t},\n\t\t\t\t\tterminate: true,\n\t\t\t\t},\n\t\t\t\t\"main\",\n\t\t\t);\n\n\t\t\tstrictEqual(entry.terminate, true);\n\t\t\tconst stored = await session.getEntry(entry.id);\n\t\t\tif (stored?.type !== \"message\") throw new Error(\"Expected message entry\");\n\t\t\tstrictEqual(stored.terminate, true);\n\t\t\tdeepStrictEqual(await session.findEntries(), [entry]);\n\t\t\tdeepStrictEqual(await session.getLog(), [{ kind: \"entry\", seq: entry.seq, entry }]);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"rejects non-JSON entries before storage mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst cyclic: { self?: unknown } = {};\n\t\t\t\tcyclic.self = cyclic;\n\n\t\t\t\tfor (const data of [\n\t\t\t\t\t{ value: undefined },\n\t\t\t\t\t[undefined],\n\t\t\t\t\t{ value: 1n },\n\t\t\t\t\t{ value: Number.NaN },\n\t\t\t\t\t{ value: new Map() },\n\t\t\t\t\tcyclic,\n\t\t\t\t]) {\n\t\t\t\t\tawait rejectsWithCode(session.appendCustomEntry(\"invalid\", data), \"invalid_payload\");\n\t\t\t\t}\n\n\t\t\t\tstrictEqual(await session.getLeafId(), null);\n\t\t\t\tdeepStrictEqual(await session.findEntries(), []);\n\t\t\t\tdeepStrictEqual(await session.getLog(), []);\n\t\t\t\tconst validId = await session.appendCustomEntry(\"valid\", { value: 1 });\n\t\t\t\tstrictEqual((await session.getEntry(validId))?.seq, 1);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"rejects non-JSON records before storage mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tfor (const [id, value] of [\n\t\t\t\t\t[\"undefined-record\", undefined],\n\t\t\t\t\t[\"bigint-record\", 1n],\n\t\t\t\t] as const) {\n\t\t\t\t\tawait rejectsWithCode(\n\t\t\t\t\t\tsession.appendRecord({\n\t\t\t\t\t\t\ttype: \"tool_started\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tlane: \"main\",\n\t\t\t\t\t\t\trunId: \"run\",\n\t\t\t\t\t\t\tassistantEntryId: \"assistant\",\n\t\t\t\t\t\t\ttoolIndex: 0,\n\t\t\t\t\t\t\ttoolCallId: \"call\",\n\t\t\t\t\t\t\ttoolName: \"example\",\n\t\t\t\t\t\t\teffectiveArgs: { value },\n\t\t\t\t\t\t\tresultEntryId: \"result\",\n\t\t\t\t\t\t\treplay: \"never\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t\"invalid_payload\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdeepStrictEqual(await session.findRecords(), []);\n\t\t\t\tdeepStrictEqual(await session.getLog(), []);\n\t\t\t\tstrictEqual(\n\t\t\t\t\t(await session.appendRecord(operationStarted(\"valid-record\", { lane: \"main\", kind: \"run\" }))).seq,\n\t\t\t\t\t1,\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"linearizes concurrent writes across two lanes\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.createLane(\"thread\", \"root\");\n\t\t\tconst completionOrder: string[] = [];\n\t\t\tconst writes = [\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"main-1\", customType: \"note\" }, \"main\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"thread-1\", customType: \"note\" }, \"thread\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"main-2\", customType: \"note\" }, \"main\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"thread-2\", customType: \"note\" }, \"thread\"),\n\t\t\t].map((write) =>\n\t\t\t\twrite.then((entry) => {\n\t\t\t\t\tcompletionOrder.push(entry.id);\n\t\t\t\t\treturn entry;\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst entries = await Promise.all(writes);\n\t\t\tconst commitOrder = [...entries].sort((left, right) => left.seq - right.seq).map((entry) => entry.id);\n\n\t\t\tstrictEqual(new Set(entries.map((entry) => entry.seq)).size, entries.length);\n\t\t\tdeepStrictEqual(completionOrder, commitOrder);\n\t\t\tconst concurrentIds = new Set(entries.map((entry) => entry.id));\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await session.getLog()).flatMap((item) =>\n\t\t\t\t\titem.kind === \"entry\" && concurrentIds.has(item.entry.id) ? [item.entry.id] : [],\n\t\t\t\t),\n\t\t\t\tcommitOrder,\n\t\t\t);\n\t\t\tconst sequences = (await session.getLog()).map((item) => item.seq);\n\t\t\tdeepStrictEqual(\n\t\t\t\tsequences,\n\t\t\t\t[...sequences].sort((left, right) => left - right),\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"repository and forks\", \"creates lists and opens sessions\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"one\" });\n\t\t\tconst entryId = await session.appendMessage(createUserMessage(\"persisted\"));\n\t\t\tconst metadata = await session.getMetadata();\n\n\t\t\tconst listed = await repository.list();\n\t\t\tstrictEqual(listed.length, 1);\n\t\t\tstrictEqual(listed[0]?.id, metadata.id);\n\t\t\tstrictEqual(listed[0]?.createdAt, metadata.createdAt);\n\t\t\tstrictEqual(listed[0]?.parentSessionId, metadata.parentSessionId);\n\t\t\tdeepStrictEqual(await entryIds((await repository.open(metadata)).findEntries()), [entryId]);\n\t\t\tawait rejectsWithCode(repository.create({ id: \"one\" }), \"already_exists\");\n\t\t}),\n\n\t\tcreateCase(factory, \"repository and forks\", \"deletes sessions idempotently\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"one\" });\n\t\t\tconst metadata = await session.getMetadata();\n\n\t\t\tawait repository.delete(metadata);\n\t\t\tawait rejectsWithCode(repository.open(metadata), \"not_found\");\n\t\t\tawait repository.delete(metadata);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"repository and forks\",\n\t\t\t\"forks one branch with selected facts and no records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\t\tconst shared = await source.appendMessage(createAssistantMessage(\"shared\"));\n\t\t\t\tawait source.createLane(\"thread\", shared);\n\t\t\t\tconst threadChild = await source.view(\"thread\").appendMessage(createUserMessage(\"thread\"));\n\t\t\t\tconst mainChild = await source.appendMessage(createUserMessage(\"main\"));\n\t\t\t\tawait source.setName(\"Source\");\n\t\t\t\tawait source.setLabel(shared, \"copied\");\n\t\t\t\tawait source.setLabel(threadChild, \"excluded\");\n\t\t\t\tawait source.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tawait source.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"source-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"adjustment\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 10,\n\t\t\t\t\t\toutput: 5,\n\t\t\t\t\t\tcacheRead: 3,\n\t\t\t\t\t\tcacheWrite: 2,\n\t\t\t\t\t\ttotalTokens: 20,\n\t\t\t\t\t\tcost: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst fork = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tscope: \"branch\",\n\t\t\t\t\tentryId: mainChild,\n\t\t\t\t\tposition: \"at\",\n\t\t\t\t\tid: \"branch-fork\",\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root, shared, mainChild]);\n\t\t\t\tdeepStrictEqual(await fork.getLanes(), [{ lane: \"main\", leafId: mainChild }]);\n\t\t\t\tstrictEqual(await fork.getName(), \"Source\");\n\t\t\t\tstrictEqual(await fork.getLabel(shared), \"copied\");\n\t\t\t\tstrictEqual(await fork.getLabel(threadChild), undefined);\n\t\t\t\tdeepStrictEqual(await fork.findRecords(), []);\n\t\t\t\tdeepStrictEqual(await fork.getStats(), {\n\t\t\t\t\tmessageCount: 3,\n\t\t\t\t\tcachedTokens: 0,\n\t\t\t\t\tuncachedTokens: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcostTotal: 0,\n\t\t\t\t});\n\t\t\t\tawait fork.appendMessage(createUserMessage(\"after fork\"));\n\t\t\t\tstrictEqual((await fork.getStats()).messageCount, 4);\n\t\t\t\tconst metadata = await fork.getMetadata();\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t{ id: metadata.id, parentSessionId: metadata.parentSessionId },\n\t\t\t\t\t{ id: \"branch-fork\", parentSessionId: \"source\" },\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"repository and forks\", \"forks a complete tree with lanes and facts\", async (repository) => {\n\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\tawait source.createLane(\"thread\", root);\n\t\t\tconst mainChild = await source.appendMessage(createUserMessage(\"main\"));\n\t\t\tconst threadChild = await source.view(\"thread\").appendMessage(createUserMessage(\"thread\"));\n\t\t\tawait source.setLabel(threadChild, \"thread-tip\");\n\n\t\t\tconst fork = await repository.fork(await source.getMetadata(), { scope: \"tree\", id: \"tree-fork\" });\n\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root, mainChild, threadChild]);\n\t\t\tdeepStrictEqual(await fork.getLanes(), [\n\t\t\t\t{ lane: \"main\", leafId: mainChild },\n\t\t\t\t{ lane: \"thread\", leafId: threadChild },\n\t\t\t]);\n\t\t\tstrictEqual(await fork.getLabel(threadChild), \"thread-tip\");\n\t\t\tstrictEqual((await fork.getStats()).messageCount, 3);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await fork.getLog()).filter((item) => item.kind === \"lane\"),\n\t\t\t\t[\n\t\t\t\t\t{ kind: \"lane\", seq: 4, lane: \"main\", leafId: mainChild },\n\t\t\t\t\t{ kind: \"lane\", seq: 5, lane: \"thread\", leafId: threadChild },\n\t\t\t\t],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"repository and forks\",\n\t\t\t\"forks before an entry without modifying the source\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\t\tconst tail = await source.appendMessage(createUserMessage(\"tail\"));\n\t\t\t\tconst fork = await repository.fork(await source.getMetadata(), { entryId: tail, id: \"fork\" });\n\n\t\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root]);\n\t\t\t\tstrictEqual(await fork.getLeafId(), root);\n\t\t\t\tstrictEqual(await source.getLeafId(), tail);\n\t\t\t\tconst beforeDefaultTarget = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tposition: \"before\",\n\t\t\t\t\tid: \"before-default-target\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual(await entryIds(beforeDefaultTarget.findEntries({ order: \"oldestFirst\" })), [root]);\n\t\t\t\tstrictEqual(await beforeDefaultTarget.getLeafId(), root);\n\n\t\t\t\tconst atDefaultTarget = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tposition: \"at\",\n\t\t\t\t\tid: \"at-default-target\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual(await entryIds(atDefaultTarget.findEntries({ order: \"oldestFirst\" })), [root, tail]);\n\t\t\t\tstrictEqual(await atDefaultTarget.getLeafId(), tail);\n\t\t\t\tawait rejectsWithCode(\n\t\t\t\t\trepository.fork(await source.getMetadata(), { entryId: \"missing\" }),\n\t\t\t\t\t\"invalid_fork_target\",\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"repository and forks\", \"validates the default fork target\", async (repository) => {\n\t\t\tconst source = await repository.create({ id: \"source-with-custom-leaf\" });\n\t\t\tawait source.appendCustomEntry(\"not-a-message\");\n\n\t\t\tawait rejectsWithCode(repository.fork(await source.getMetadata(), { id: \"fork\" }), \"invalid_fork_target\");\n\t\t}),\n\t];\n}\n"]}
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/testing/conformance.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AA+E9F,qGAAqG;AACrG,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,4BAA4B,GACnC,SAAS,6BAA6B,EAAE,CA05B1C","sourcesContent":["import { deepStrictEqual, ok, rejects, strictEqual } from \"node:assert/strict\";\nimport type { AgentMessage } from \"../../../types.ts\";\nimport type {\n\tCustomEntry,\n\tEntry,\n\tMessageEntry,\n\tNewRecord,\n\tOperationStartedRecord,\n\tSessionErrorCode,\n\tSessionRepo,\n} from \"../types.ts\";\nimport type { SessionBackendConformanceCase, SessionBackendFixtureFactory } from \"./types.ts\";\n\nfunction createUserMessage(text: string): AgentMessage {\n\treturn {\n\t\trole: \"user\",\n\t\tcontent: [{ type: \"text\", text }],\n\t\ttimestamp: 1,\n\t};\n}\n\nfunction createAssistantMessage(text: string): AgentMessage {\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: [{ type: \"text\", text }],\n\t\tapi: \"anthropic-messages\",\n\t\tprovider: \"anthropic\",\n\t\tmodel: \"claude-sonnet-4-5\",\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: 1,\n\t};\n}\n\nfunction operationStarted(\n\tid: string,\n\t{ lane, kind }: { lane: string; kind: OperationStartedRecord[\"intent\"][\"kind\"] },\n): NewRecord<OperationStartedRecord> {\n\tlet intent: OperationStartedRecord[\"intent\"];\n\tswitch (kind) {\n\t\tcase \"run\":\n\t\t\tintent = { kind, originalPrompt: [], initialMessages: [] };\n\t\t\tbreak;\n\t\tcase \"compaction\":\n\t\t\tintent = { kind, resultEntryId: `${id}-result` };\n\t\t\tbreak;\n\t\tcase \"navigation\":\n\t\t\tintent = { kind, targetId: null, summarize: false };\n\t\t\tbreak;\n\t}\n\treturn { type: \"operation_started\", id, lane, sourceLeafId: null, intent };\n}\n\nasync function entryIds(entries: Promise<Entry[]>): Promise<string[]> {\n\treturn (await entries).map((entry) => entry.id);\n}\n\nasync function rejectsWithCode(operation: Promise<unknown>, code: SessionErrorCode): Promise<void> {\n\tawait rejects(\n\t\toperation,\n\t\t(error: unknown) => typeof error === \"object\" && error !== null && \"code\" in error && error.code === code,\n\t\t`Expected SessionError with code ${code}`,\n\t);\n}\n\ntype ConformanceTest = (repository: SessionRepo) => Promise<void>;\n\nfunction createCase(\n\tfactory: SessionBackendFixtureFactory,\n\tgroup: string,\n\tname: string,\n\ttest: ConformanceTest,\n): SessionBackendConformanceCase {\n\treturn {\n\t\tgroup,\n\t\tname,\n\t\tasync run() {\n\t\t\tawait using fixture = await factory();\n\t\t\tawait test(fixture.repository);\n\t\t},\n\t};\n}\n\n/** Creates the session backend conformance cases. Each case creates and disposes its own fixture. */\nexport function createSessionBackendConformance(\n\tfactory: SessionBackendFixtureFactory,\n): readonly SessionBackendConformanceCase[] {\n\treturn [\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"entries and lanes\",\n\t\t\t\"assigns parents and one sequence across every mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst root = await session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.createLane(\"thread\", root.id);\n\t\t\t\tconst child = await session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"child\", customType: \"note\", data: { value: 1 } },\n\t\t\t\t\t\"thread\",\n\t\t\t\t);\n\t\t\t\tconst record = await session.appendRecord(operationStarted(\"run\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.setName(\"Example\");\n\t\t\t\tawait session.setLabel(root.id, \"checkpoint\");\n\t\t\t\tawait session.moveLane(\"main\", child.id);\n\n\t\t\t\tdeepStrictEqual({ parentId: root.parentId, seq: root.seq }, { parentId: null, seq: 1 });\n\t\t\t\tdeepStrictEqual({ parentId: child.parentId, seq: child.seq }, { parentId: \"root\", seq: 3 });\n\t\t\t\tstrictEqual(record.seq, 4);\n\t\t\t\tfor (const timestamp of [root.timestamp, child.timestamp, record.timestamp]) {\n\t\t\t\t\tok(\n\t\t\t\t\t\tNumber.isSafeInteger(timestamp) && timestamp >= 0,\n\t\t\t\t\t\t\"storage-assigned timestamps must be Unix milliseconds\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).map((item) => [item.kind, item.seq]),\n\t\t\t\t\t[\n\t\t\t\t\t\t[\"entry\", 1],\n\t\t\t\t\t\t[\"lane\", 2],\n\t\t\t\t\t\t[\"entry\", 3],\n\t\t\t\t\t\t[\"record\", 4],\n\t\t\t\t\t\t[\"fact\", 5],\n\t\t\t\t\t\t[\"fact\", 6],\n\t\t\t\t\t\t[\"lane\", 7],\n\t\t\t\t\t],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [\n\t\t\t\t\t{ lane: \"main\", leafId: \"child\" },\n\t\t\t\t\t{ lane: \"thread\", leafId: \"child\" },\n\t\t\t\t]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"commits records and lane moves as separate mutations\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst root = await session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tconst finished = await session.appendRecord({\n\t\t\t\t\ttype: \"operation_finished\",\n\t\t\t\t\tid: \"finish\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\toutcome: \"completed\",\n\t\t\t\t});\n\n\t\t\t\tstrictEqual(finished.seq, 2);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [{ lane: \"main\", leafId: \"root\" }]);\n\t\t\t\tawait session.moveLane(\"main\", null);\n\t\t\t\tdeepStrictEqual(await session.getLanes(), [{ lane: \"main\", leafId: null }]);\n\t\t\t\tdeepStrictEqual(await session.getLog(), [\n\t\t\t\t\t{ kind: \"entry\", seq: 1, entry: root },\n\t\t\t\t\t{ kind: \"record\", seq: 2, record: finished },\n\t\t\t\t\t{ kind: \"lane\", seq: 3, lane: \"main\", leafId: null },\n\t\t\t\t]);\n\n\t\t\t\tawait rejectsWithCode(session.moveLane(\"main\", \"missing\"), \"not_found\");\n\t\t\t\tstrictEqual((await session.findRecords()).length, 1);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).map((item) => item.seq),\n\t\t\t\t\t[1, 2, 3],\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"rejects duplicate ids without changing state\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"shared\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendRecord(operationStarted(\"shared\", { lane: \"main\", kind: \"run\" })),\n\t\t\t\t\"already_exists\",\n\t\t\t);\n\t\t\tawait session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"run\", customType: \"note\" }, \"main\"),\n\t\t\t\t\"already_exists\",\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await session.getLog()).map((item) => item.seq),\n\t\t\t\t[1, 2],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"isolates lanes while sharing the tree\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.createLane(\"thread\", \"root\");\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"main-child\", message: createUserMessage(\"main\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"thread-child\", message: createUserMessage(\"thread\") },\n\t\t\t\t\"thread\",\n\t\t\t);\n\n\t\t\tdeepStrictEqual(await session.getLanes(), [\n\t\t\t\t{ lane: \"main\", leafId: \"main-child\" },\n\t\t\t\t{ lane: \"thread\", leafId: \"thread-child\" },\n\t\t\t]);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ start: \"main-child\", order: \"oldestFirst\" })), [\n\t\t\t\t\"root\",\n\t\t\t\t\"main-child\",\n\t\t\t]);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ start: \"thread-child\", order: \"oldestFirst\" })), [\n\t\t\t\t\"root\",\n\t\t\t\t\"thread-child\",\n\t\t\t]);\n\t\t}),\n\n\t\tcreateCase(factory, \"queries and facts\", \"rejects invalid queries before empty reads\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"invalid-queries\" });\n\t\t\tawait session.createLane(\"thread\", null);\n\t\t\tconst thread = session.view(\"thread\");\n\n\t\t\tawait rejectsWithCode(session.findEntries({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findEntry({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findEntriesOnBranch({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(thread.findEntriesOnBranch({ cursor: { afterSeq: -1 } }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(thread.findEntryOnBranch({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ operationKind: \"run\" }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findRecords({ type: \"step_attempt\", operationKind: \"run\" }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findOpenOperations(\"main\", { limit: 0 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.findOpenOperations(\"main\", { limit: -1 }), \"invalid_query\");\n\t\t\tawait rejectsWithCode(session.getLog({ afterSeq: -1 }), \"invalid_query\");\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"queries and facts\",\n\t\t\t\"supports bounded filtered and cursor-based queries\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"old-note\", customType: \"note\", data: 1 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry(\n\t\t\t\t\t{ type: \"compaction\", id: \"compact\", summary: \"summary\", retainedTail: [], tokensBefore: 10 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"new-note\", customType: \"note\", data: 2 },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"tail\", message: createAssistantMessage(\"tail\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\n\t\t\t\tdeepStrictEqual(await entryIds(session.findEntries()), [\"tail\", \"new-note\", \"compact\", \"old-note\", \"root\"]);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntries({ order: \"oldestFirst\", cursor: { afterSeq: 2 }, limit: 2 })),\n\t\t\t\t\t[\"compact\", \"new-note\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(await entryIds(session.findEntries({ customType: \"note\" })), [\"new-note\", \"old-note\"]);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntriesOnBranch({ start: \"tail\", customType: \"note\", limit: 1 })),\n\t\t\t\t\t[\"new-note\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(\n\t\t\t\t\t\tsession.findEntriesOnBranch({ start: \"tail\", stopAtType: \"compaction\", type: \"message\" }),\n\t\t\t\t\t),\n\t\t\t\t\t[\"tail\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(session.findEntriesOnBranch({ start: \"tail\", stopAtId: \"tail\", type: \"custom\" })),\n\t\t\t\t\t[],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tawait entryIds(\n\t\t\t\t\t\tsession.findEntriesOnBranch({ start: \"tail\", stopAtType: \"custom\", order: \"oldestFirst\" }),\n\t\t\t\t\t),\n\t\t\t\t\t[\"root\", \"old-note\"],\n\t\t\t\t);\n\t\t\t\tawait rejectsWithCode(session.findEntries({ limit: 0 }), \"invalid_query\");\n\t\t\t\tawait rejectsWithCode(session.findEntriesOnBranch({ start: \"missing\" }), \"not_found\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"keeps lane names permanent with their recovery records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.createLane(\"thread\", null);\n\t\t\t\tawait session.appendRecord(operationStarted(\"old-run\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"queue_enqueued\",\n\t\t\t\t\tid: \"old-next-run\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\tqueue: \"nextRun\",\n\t\t\t\t\ttarget: { type: \"message\", id: \"queued-message\", message: createUserMessage(\"queued\") },\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ lane: \"thread\" })).map((record) => record.id),\n\t\t\t\t\t[\"old-next-run\", \"old-run\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.getLog()).flatMap((item) => (item.kind === \"record\" ? [item.record.id] : [])),\n\t\t\t\t\t[\"old-run\", \"old-next-run\"],\n\t\t\t\t);\n\t\t\t\tawait rejectsWithCode(session.createLane(\"thread\", null), \"already_exists\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"persists queue cancellation without consuming its target\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst enqueued = await session.appendRecord({\n\t\t\t\t\ttype: \"queue_enqueued\",\n\t\t\t\t\tid: \"enqueue\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tqueue: \"nextRun\",\n\t\t\t\t\ttarget: { type: \"message\", id: \"queued-message\", message: createUserMessage(\"queued\") },\n\t\t\t\t});\n\t\t\t\tconst cancelled = await session.appendRecord({\n\t\t\t\t\ttype: \"queue_cancelled\",\n\t\t\t\t\tid: \"cancel\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tentryId: \"queued-message\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual({ seq: cancelled.seq, entryId: cancelled.entryId }, { seq: 2, entryId: \"queued-message\" });\n\t\t\t\tstrictEqual(\"runId\" in cancelled, false);\n\t\t\t\tstrictEqual(await session.getEntry(\"queued-message\"), undefined);\n\t\t\t\tconst cancellations = await session.findRecords({ type: \"queue_cancelled\" });\n\t\t\t\tstrictEqual(cancellations[0]?.entryId, \"queued-message\");\n\t\t\t\tdeepStrictEqual(cancellations, [cancelled]);\n\t\t\t\tdeepStrictEqual(await session.getLog(), [\n\t\t\t\t\t{ kind: \"record\", seq: enqueued.seq, record: enqueued },\n\t\t\t\t\t{ kind: \"record\", seq: cancelled.seq, record: cancelled },\n\t\t\t\t]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"filters records by lane type run sequence and order\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendRecord(operationStarted(\"run-1\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"step_attempt\",\n\t\t\t\t\tid: \"attempt-1\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run-1\",\n\t\t\t\t\tstep: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tresultEntryId: \"assistant-1\",\n\t\t\t\t});\n\t\t\t\tawait session.createLane(\"thread\", null);\n\t\t\t\tawait session.appendRecord(operationStarted(\"run-2\", { lane: \"thread\", kind: \"run\" }));\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"step_attempt\",\n\t\t\t\t\tid: \"attempt-2\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\trunId: \"run-2\",\n\t\t\t\t\tstep: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tresultEntryId: \"assistant-2\",\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ lane: \"thread\" })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-2\", \"run-2\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ type: \"step_attempt\", order: \"oldestFirst\" })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-1\", \"attempt-2\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ runId: \"run-1\", afterSeq: 1 })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-1\"],\n\t\t\t\t);\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t(await session.findRecords({ limit: 1 })).map((record) => record.id),\n\t\t\t\t\t[\"attempt-2\"],\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"records and log\", \"filters operation starts by operation kind\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendRecord(operationStarted(\"run-old\", { lane: \"main\", kind: \"run\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"run-old-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"run-old\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"compaction\", { lane: \"main\", kind: \"compaction\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"compaction-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"compaction\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"navigation\", { lane: \"main\", kind: \"navigation\" }));\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"navigation-finished\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: \"navigation\",\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tawait session.appendRecord(operationStarted(\"run-new\", { lane: \"main\", kind: \"run\" }));\n\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"run\",\n\t\t\t\t\t\torder: \"oldestFirst\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"run-old\", \"run-new\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"compaction\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"compaction\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"navigation\",\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"navigation\"],\n\t\t\t);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(\n\t\t\t\t\tawait session.findRecords({\n\t\t\t\t\t\ttype: \"operation_started\",\n\t\t\t\t\t\toperationKind: \"run\",\n\t\t\t\t\t\tlimit: 1,\n\t\t\t\t\t})\n\t\t\t\t).map((record) => record.id),\n\t\t\t\t[\"run-new\"],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"records and log\", \"tracks and enforces one open operation per lane\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), []);\n\n\t\t\tconst first = await session.appendRecord(operationStarted(\"first\", { lane: \"main\", kind: \"run\" }));\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [first]);\n\t\t\tawait rejectsWithCode(\n\t\t\t\tsession.appendRecord(operationStarted(\"second\", { lane: \"main\", kind: \"run\" })),\n\t\t\t\t\"storage\",\n\t\t\t);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [first]);\n\n\t\t\tawait session.appendRecord({\n\t\t\t\ttype: \"operation_finished\",\n\t\t\t\tid: \"finish-first\",\n\t\t\t\tlane: \"main\",\n\t\t\t\trunId: first.id,\n\t\t\t\toutcome: \"completed\",\n\t\t\t});\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), []);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"records and log\",\n\t\t\t\"does not let an earlier finish close a later start\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"operation_finished\",\n\t\t\t\t\tid: \"finish-before-start\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\toutcome: \"completed\",\n\t\t\t\t});\n\t\t\t\tconst started = await session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 2 }), [started]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"records and log\", \"scopes open operations by lane and limit\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.createLane(\"thread\", null);\n\t\t\tconst mainRun = await session.appendRecord(operationStarted(\"main-run\", { lane: \"main\", kind: \"run\" }));\n\t\t\tconst threadNavigation = await session.appendRecord(\n\t\t\t\toperationStarted(\"thread-navigation\", { lane: \"thread\", kind: \"navigation\" }),\n\t\t\t);\n\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\"), [mainRun]);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\", { limit: 1 }), [mainRun]);\n\t\t\tdeepStrictEqual(await session.findOpenOperations(\"thread\", { limit: 2 }), [threadNavigation]);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"returns immutable open-operation records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst committed = await session.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tconst [read] = await session.findOpenOperations(\"main\");\n\t\t\t\tif (read?.intent.kind !== \"run\") throw new Error(\"Expected an open run operation\");\n\t\t\t\tread.intent.originalPrompt.push(createUserMessage(\"mutated\"));\n\n\t\t\t\tdeepStrictEqual(await session.findOpenOperations(\"main\"), [committed]);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"queries and facts\",\n\t\t\t\"keeps latest-value facts and computes ledger statistics across lanes\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst assistant = createAssistantMessage(\"answer\");\n\t\t\t\tif (assistant.role !== \"assistant\") throw new Error(\"Expected assistant message\");\n\t\t\t\tassistant.usage = {\n\t\t\t\t\tinput: 10,\n\t\t\t\t\toutput: 5,\n\t\t\t\t\tcacheRead: 3,\n\t\t\t\t\tcacheWrite: 2,\n\t\t\t\t\ttotalTokens: 20,\n\t\t\t\t\tcost: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },\n\t\t\t\t};\n\t\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t\t{ type: \"message\", id: \"user\", message: createUserMessage(\"question\") },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\t\t\t\tawait session.appendEntry<MessageEntry>({ type: \"message\", id: \"assistant\", message: assistant }, \"main\");\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"assistant-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"assistant\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\tentryId: \"assistant\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tstopReason: \"stop\",\n\t\t\t\t\tusage: assistant.usage,\n\t\t\t\t});\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"deferred-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"deferred_fetch\",\n\t\t\t\t\trunId: \"run\",\n\t\t\t\t\tentryId: \"deferred-result\",\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tstopReason: \"deferred\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tawait session.createLane(\"thread\", \"assistant\");\n\t\t\t\tawait session.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"correction\",\n\t\t\t\t\tlane: \"thread\",\n\t\t\t\t\tcause: \"adjustment\",\n\t\t\t\t\tdetails: { reason: \"provider correction\" },\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: -2,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\ttotalTokens: -2,\n\t\t\t\t\t\tcost: { input: -0.5, output: 0, cacheRead: 0, cacheWrite: 0, total: -0.5 },\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tawait session.setName(\"First\");\n\t\t\t\tawait session.setName(\"Second\");\n\t\t\t\tawait session.setLabel(\"user\", \"keep\");\n\t\t\t\tawait session.setLabel(\"user\", undefined);\n\t\t\t\tawait rejectsWithCode(session.setLabel(\"missing\", \"checkpoint\"), \"not_found\");\n\n\t\t\t\tstrictEqual(await session.getName(), \"Second\");\n\t\t\t\tstrictEqual(await session.getLabel(\"user\"), undefined);\n\t\t\t\tconst usageRecords = await session.findRecords({ type: \"usage\", order: \"oldestFirst\" });\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\tusageRecords.map((record) => record.cause),\n\t\t\t\t\t[\"assistant\", \"deferred_fetch\", \"adjustment\"],\n\t\t\t\t);\n\t\t\t\tconst deferredUsage = usageRecords.find((record) => record.cause === \"deferred_fetch\");\n\t\t\t\tif (deferredUsage?.cause !== \"deferred_fetch\") throw new Error(\"Expected deferred usage record\");\n\t\t\t\tstrictEqual(deferredUsage.stopReason, \"deferred\");\n\t\t\t\tdeepStrictEqual(await session.getStats(), {\n\t\t\t\t\tmessageCount: 2,\n\t\t\t\t\tcachedTokens: 3,\n\t\t\t\t\tuncachedTokens: 10,\n\t\t\t\t\ttotalTokens: 18,\n\t\t\t\t\tcostTotal: 9.5,\n\t\t\t\t});\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"queries and facts\", \"clears session names durably\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.setName(\"Temporary\");\n\t\t\tawait session.setName(undefined);\n\n\t\t\tstrictEqual(await session.getName(), undefined);\n\t\t\tdeepStrictEqual(await session.getLog(), [\n\t\t\t\t{ kind: \"fact\", seq: 1, fact: \"name\", name: \"Temporary\" },\n\t\t\t\t{ kind: \"fact\", seq: 2, fact: \"name\", name: undefined },\n\t\t\t]);\n\n\t\t\tconst metadata = await session.getMetadata();\n\t\t\tconst reopened = await repository.open(metadata);\n\t\t\tstrictEqual(await reopened.getName(), undefined);\n\t\t\tdeepStrictEqual(await reopened.getLog(), [\n\t\t\t\t{ kind: \"fact\", seq: 1, fact: \"name\", name: \"Temporary\" },\n\t\t\t\t{ kind: \"fact\", seq: 2, fact: \"name\", name: undefined },\n\t\t\t]);\n\n\t\t\tconst fork = await repository.fork(metadata, { id: \"fork\" });\n\t\t\tstrictEqual(await fork.getName(), undefined);\n\t\t}),\n\n\t\tcreateCase(factory, \"validation and immutability\", \"returns immutable copies from reads\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"immutable\" });\n\t\t\tconst metadata = await session.getMetadata();\n\t\t\tconst data = { nested: { value: 1 } };\n\t\t\tawait session.appendEntry<CustomEntry>({ type: \"custom\", id: \"custom\", customType: \"note\", data }, \"main\");\n\t\t\tdata.nested.value = 50;\n\t\t\tconst read = await session.getEntry(\"custom\");\n\t\t\tif (read?.type !== \"custom\") throw new Error(\"Expected custom entry\");\n\t\t\t(read.data as { nested: { value: number } }).nested.value = 99;\n\t\t\tconst readMetadata = await session.getMetadata();\n\t\t\treadMetadata.id = \"changed\";\n\t\t\tconst log = await session.getLog();\n\t\t\tif (log[0]?.kind !== \"entry\" || log[0].entry.type !== \"custom\") throw new Error(\"Expected entry log\");\n\t\t\t(log[0].entry.data as { nested: { value: number } }).nested.value = 100;\n\n\t\t\tdeepStrictEqual(await session.getMetadata(), metadata);\n\t\t\tdeepStrictEqual(await session.getEntry(\"custom\"), {\n\t\t\t\ttype: \"custom\",\n\t\t\t\tid: \"custom\",\n\t\t\t\tcustomType: \"note\",\n\t\t\t\tdata: { nested: { value: 1 } },\n\t\t\t\tparentId: null,\n\t\t\t\tseq: 1,\n\t\t\t\ttimestamp: read.timestamp,\n\t\t\t});\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"validates lane lifecycle and targets\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait rejectsWithCode(session.createLane(\"main\", null), \"already_exists\");\n\t\t\tawait rejectsWithCode(session.createLane(\"thread\", \"missing\"), \"not_found\");\n\t\t\tawait rejectsWithCode(session.moveLane(\"missing\", null), \"invalid_lane\");\n\t\t}),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"binds lane views without caching leaves\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tconst root = await session.appendMessage(createUserMessage(\"root\"));\n\t\t\tawait session.createLane(\"thread\", root);\n\t\t\tconst thread = session.view(\"thread\");\n\t\t\tconst [mainChild, threadChild] = await Promise.all([\n\t\t\t\tsession.appendMessage(createUserMessage(\"main\")),\n\t\t\t\tthread.appendMessage(createUserMessage(\"thread\")),\n\t\t\t]);\n\n\t\t\tstrictEqual(await session.getLeafId(), mainChild);\n\t\t\tstrictEqual(await thread.getLeafId(), threadChild);\n\t\t\tdeepStrictEqual(await entryIds(session.findEntriesOnBranch({ order: \"oldestFirst\" })), [root, mainChild]);\n\t\t\tdeepStrictEqual(await entryIds(thread.findEntriesOnBranch({ order: \"oldestFirst\" })), [root, threadChild]);\n\t\t\tconst empty = await repository.create({ id: \"empty\" });\n\t\t\tdeepStrictEqual(await empty.findEntriesOnBranch(), []);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"entries and lanes\",\n\t\t\t\"appends provisioned entries with their existing ids\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst entry = await session.appendEntry<CustomEntry>(\n\t\t\t\t\t{ type: \"custom\", id: \"provisioned\", customType: \"note\", data: { value: 1 } },\n\t\t\t\t\t\"main\",\n\t\t\t\t);\n\n\t\t\t\tstrictEqual(entry.customType, \"note\");\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t{ id: entry.id, parentId: entry.parentId, seq: entry.seq },\n\t\t\t\t\t{ id: \"provisioned\", parentId: null, seq: 1 },\n\t\t\t\t);\n\t\t\t\tstrictEqual(await session.getLeafId(), \"provisioned\");\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"persists tool-result termination decisions\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tconst entry = await session.appendEntry<MessageEntry>(\n\t\t\t\t{\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t\tid: \"tool-result\",\n\t\t\t\t\tmessage: {\n\t\t\t\t\t\trole: \"toolResult\",\n\t\t\t\t\t\ttoolCallId: \"call-1\",\n\t\t\t\t\t\ttoolName: \"example\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: \"done\" }],\n\t\t\t\t\t\tisError: false,\n\t\t\t\t\t\ttimestamp: 1,\n\t\t\t\t\t},\n\t\t\t\t\tterminate: true,\n\t\t\t\t},\n\t\t\t\t\"main\",\n\t\t\t);\n\n\t\t\tstrictEqual(entry.terminate, true);\n\t\t\tconst stored = await session.getEntry(entry.id);\n\t\t\tif (stored?.type !== \"message\") throw new Error(\"Expected message entry\");\n\t\t\tstrictEqual(stored.terminate, true);\n\t\t\tdeepStrictEqual(await session.findEntries(), [entry]);\n\t\t\tdeepStrictEqual(await session.getLog(), [{ kind: \"entry\", seq: entry.seq, entry }]);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"rejects non-JSON entries before storage mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tconst cyclic: { self?: unknown } = {};\n\t\t\t\tcyclic.self = cyclic;\n\n\t\t\t\tfor (const data of [\n\t\t\t\t\t{ value: undefined },\n\t\t\t\t\t[undefined],\n\t\t\t\t\t{ value: 1n },\n\t\t\t\t\t{ value: Number.NaN },\n\t\t\t\t\t{ value: new Map() },\n\t\t\t\t\tcyclic,\n\t\t\t\t]) {\n\t\t\t\t\tawait rejectsWithCode(session.appendCustomEntry(\"invalid\", data), \"invalid_payload\");\n\t\t\t\t}\n\n\t\t\t\tstrictEqual(await session.getLeafId(), null);\n\t\t\t\tdeepStrictEqual(await session.findEntries(), []);\n\t\t\t\tdeepStrictEqual(await session.getLog(), []);\n\t\t\t\tconst validId = await session.appendCustomEntry(\"valid\", { value: 1 });\n\t\t\t\tstrictEqual((await session.getEntry(validId))?.seq, 1);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"validation and immutability\",\n\t\t\t\"rejects non-JSON records before storage mutation\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\t\tfor (const [id, value] of [\n\t\t\t\t\t[\"undefined-record\", undefined],\n\t\t\t\t\t[\"bigint-record\", 1n],\n\t\t\t\t] as const) {\n\t\t\t\t\tawait rejectsWithCode(\n\t\t\t\t\t\tsession.appendRecord({\n\t\t\t\t\t\t\ttype: \"tool_started\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\tlane: \"main\",\n\t\t\t\t\t\t\trunId: \"run\",\n\t\t\t\t\t\t\tassistantEntryId: \"assistant\",\n\t\t\t\t\t\t\ttoolIndex: 0,\n\t\t\t\t\t\t\ttoolCallId: \"call\",\n\t\t\t\t\t\t\ttoolName: \"example\",\n\t\t\t\t\t\t\teffectiveArgs: { value },\n\t\t\t\t\t\t\tresultEntryId: \"result\",\n\t\t\t\t\t\t\treplay: \"never\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t\"invalid_payload\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdeepStrictEqual(await session.findRecords(), []);\n\t\t\t\tdeepStrictEqual(await session.getLog(), []);\n\t\t\t\tstrictEqual(\n\t\t\t\t\t(await session.appendRecord(operationStarted(\"valid-record\", { lane: \"main\", kind: \"run\" }))).seq,\n\t\t\t\t\t1,\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"entries and lanes\", \"linearizes concurrent writes across two lanes\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"session\" });\n\t\t\tawait session.appendEntry<MessageEntry>(\n\t\t\t\t{ type: \"message\", id: \"root\", message: createUserMessage(\"root\") },\n\t\t\t\t\"main\",\n\t\t\t);\n\t\t\tawait session.createLane(\"thread\", \"root\");\n\t\t\tconst completionOrder: string[] = [];\n\t\t\tconst writes = [\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"main-1\", customType: \"note\" }, \"main\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"thread-1\", customType: \"note\" }, \"thread\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"main-2\", customType: \"note\" }, \"main\"),\n\t\t\t\tsession.appendEntry<CustomEntry>({ type: \"custom\", id: \"thread-2\", customType: \"note\" }, \"thread\"),\n\t\t\t].map((write) =>\n\t\t\t\twrite.then((entry) => {\n\t\t\t\t\tcompletionOrder.push(entry.id);\n\t\t\t\t\treturn entry;\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst entries = await Promise.all(writes);\n\t\t\tconst commitOrder = [...entries].sort((left, right) => left.seq - right.seq).map((entry) => entry.id);\n\n\t\t\tstrictEqual(new Set(entries.map((entry) => entry.seq)).size, entries.length);\n\t\t\tdeepStrictEqual(completionOrder, commitOrder);\n\t\t\tconst concurrentIds = new Set(entries.map((entry) => entry.id));\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await session.getLog()).flatMap((item) =>\n\t\t\t\t\titem.kind === \"entry\" && concurrentIds.has(item.entry.id) ? [item.entry.id] : [],\n\t\t\t\t),\n\t\t\t\tcommitOrder,\n\t\t\t);\n\t\t\tconst sequences = (await session.getLog()).map((item) => item.seq);\n\t\t\tdeepStrictEqual(\n\t\t\t\tsequences,\n\t\t\t\t[...sequences].sort((left, right) => left - right),\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(factory, \"repository and forks\", \"creates lists and opens sessions\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"one\" });\n\t\t\tconst entryId = await session.appendMessage(createUserMessage(\"persisted\"));\n\t\t\tconst metadata = await session.getMetadata();\n\n\t\t\tconst listed = await repository.list();\n\t\t\tstrictEqual(listed.length, 1);\n\t\t\tstrictEqual(listed[0]?.id, metadata.id);\n\t\t\tstrictEqual(listed[0]?.createdAt, metadata.createdAt);\n\t\t\tstrictEqual(listed[0]?.parentSessionId, metadata.parentSessionId);\n\t\t\tdeepStrictEqual(await entryIds((await repository.open(metadata)).findEntries()), [entryId]);\n\t\t\tawait rejectsWithCode(repository.create({ id: \"one\" }), \"already_exists\");\n\t\t}),\n\n\t\tcreateCase(factory, \"repository and forks\", \"deletes sessions idempotently\", async (repository) => {\n\t\t\tconst session = await repository.create({ id: \"one\" });\n\t\t\tconst metadata = await session.getMetadata();\n\n\t\t\tawait repository.delete(metadata);\n\t\t\tawait rejectsWithCode(repository.open(metadata), \"not_found\");\n\t\t\tawait repository.delete(metadata);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"repository and forks\",\n\t\t\t\"forks one branch with selected facts and no records\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\t\tconst shared = await source.appendMessage(createAssistantMessage(\"shared\"));\n\t\t\t\tawait source.createLane(\"thread\", shared);\n\t\t\t\tconst threadChild = await source.view(\"thread\").appendMessage(createUserMessage(\"thread\"));\n\t\t\t\tconst mainChild = await source.appendMessage(createUserMessage(\"main\"));\n\t\t\t\tawait source.setName(\"Source\");\n\t\t\t\tawait source.setLabel(shared, \"copied\");\n\t\t\t\tawait source.setLabel(threadChild, \"excluded\");\n\t\t\t\tawait source.appendRecord(operationStarted(\"run\", { lane: \"main\", kind: \"run\" }));\n\t\t\t\tawait source.appendRecord({\n\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\tid: \"source-usage\",\n\t\t\t\t\tlane: \"main\",\n\t\t\t\t\tcause: \"adjustment\",\n\t\t\t\t\tusage: {\n\t\t\t\t\t\tinput: 10,\n\t\t\t\t\t\toutput: 5,\n\t\t\t\t\t\tcacheRead: 3,\n\t\t\t\t\t\tcacheWrite: 2,\n\t\t\t\t\t\ttotalTokens: 20,\n\t\t\t\t\t\tcost: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst fork = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tscope: \"branch\",\n\t\t\t\t\tentryId: mainChild,\n\t\t\t\t\tposition: \"at\",\n\t\t\t\t\tid: \"branch-fork\",\n\t\t\t\t});\n\n\t\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root, shared, mainChild]);\n\t\t\t\tdeepStrictEqual(await fork.getLanes(), [{ lane: \"main\", leafId: mainChild }]);\n\t\t\t\tstrictEqual(await fork.getName(), \"Source\");\n\t\t\t\tstrictEqual(await fork.getLabel(shared), \"copied\");\n\t\t\t\tstrictEqual(await fork.getLabel(threadChild), undefined);\n\t\t\t\tdeepStrictEqual(await fork.findRecords(), []);\n\t\t\t\tdeepStrictEqual(await fork.getStats(), {\n\t\t\t\t\tmessageCount: 3,\n\t\t\t\t\tcachedTokens: 0,\n\t\t\t\t\tuncachedTokens: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcostTotal: 0,\n\t\t\t\t});\n\t\t\t\tawait fork.appendMessage(createUserMessage(\"after fork\"));\n\t\t\t\tstrictEqual((await fork.getStats()).messageCount, 4);\n\t\t\t\tconst metadata = await fork.getMetadata();\n\t\t\t\tdeepStrictEqual(\n\t\t\t\t\t{ id: metadata.id, parentSessionId: metadata.parentSessionId },\n\t\t\t\t\t{ id: \"branch-fork\", parentSessionId: \"source\" },\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"repository and forks\", \"forks a complete tree with lanes and facts\", async (repository) => {\n\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\tawait source.createLane(\"thread\", root);\n\t\t\tconst mainChild = await source.appendMessage(createUserMessage(\"main\"));\n\t\t\tconst threadChild = await source.view(\"thread\").appendMessage(createUserMessage(\"thread\"));\n\t\t\tawait source.setLabel(threadChild, \"thread-tip\");\n\n\t\t\tconst fork = await repository.fork(await source.getMetadata(), { scope: \"tree\", id: \"tree-fork\" });\n\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root, mainChild, threadChild]);\n\t\t\tdeepStrictEqual(await fork.getLanes(), [\n\t\t\t\t{ lane: \"main\", leafId: mainChild },\n\t\t\t\t{ lane: \"thread\", leafId: threadChild },\n\t\t\t]);\n\t\t\tstrictEqual(await fork.getLabel(threadChild), \"thread-tip\");\n\t\t\tstrictEqual((await fork.getStats()).messageCount, 3);\n\t\t\tdeepStrictEqual(\n\t\t\t\t(await fork.getLog()).filter((item) => item.kind === \"lane\"),\n\t\t\t\t[\n\t\t\t\t\t{ kind: \"lane\", seq: 4, lane: \"main\", leafId: mainChild },\n\t\t\t\t\t{ kind: \"lane\", seq: 5, lane: \"thread\", leafId: threadChild },\n\t\t\t\t],\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(\n\t\t\tfactory,\n\t\t\t\"repository and forks\",\n\t\t\t\"forks before an entry without modifying the source\",\n\t\t\tasync (repository) => {\n\t\t\t\tconst source = await repository.create({ id: \"source\" });\n\t\t\t\tconst root = await source.appendMessage(createUserMessage(\"root\"));\n\t\t\t\tconst tail = await source.appendMessage(createUserMessage(\"tail\"));\n\t\t\t\tconst fork = await repository.fork(await source.getMetadata(), { entryId: tail, id: \"fork\" });\n\n\t\t\t\tdeepStrictEqual(await entryIds(fork.findEntries({ order: \"oldestFirst\" })), [root]);\n\t\t\t\tstrictEqual(await fork.getLeafId(), root);\n\t\t\t\tstrictEqual(await source.getLeafId(), tail);\n\t\t\t\tconst beforeDefaultTarget = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tposition: \"before\",\n\t\t\t\t\tid: \"before-default-target\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual(await entryIds(beforeDefaultTarget.findEntries({ order: \"oldestFirst\" })), [root]);\n\t\t\t\tstrictEqual(await beforeDefaultTarget.getLeafId(), root);\n\n\t\t\t\tconst atDefaultTarget = await repository.fork(await source.getMetadata(), {\n\t\t\t\t\tposition: \"at\",\n\t\t\t\t\tid: \"at-default-target\",\n\t\t\t\t});\n\t\t\t\tdeepStrictEqual(await entryIds(atDefaultTarget.findEntries({ order: \"oldestFirst\" })), [root, tail]);\n\t\t\t\tstrictEqual(await atDefaultTarget.getLeafId(), tail);\n\t\t\t\tawait rejectsWithCode(\n\t\t\t\t\trepository.fork(await source.getMetadata(), { entryId: \"missing\" }),\n\t\t\t\t\t\"invalid_fork_target\",\n\t\t\t\t);\n\t\t\t},\n\t\t),\n\n\t\tcreateCase(factory, \"repository and forks\", \"validates the default fork target\", async (repository) => {\n\t\t\tconst source = await repository.create({ id: \"source-with-custom-leaf\" });\n\t\t\tawait source.appendCustomEntry(\"not-a-message\");\n\n\t\t\tawait rejectsWithCode(repository.fork(await source.getMetadata(), { id: \"fork\" }), \"invalid_fork_target\");\n\t\t}),\n\t];\n}\n"]}
@@ -477,6 +477,25 @@ export function createSessionBackendConformance(factory) {
477
477
  costTotal: 9.5,
478
478
  });
479
479
  }),
480
+ createCase(factory, "queries and facts", "clears session names durably", async (repository) => {
481
+ const session = await repository.create({ id: "session" });
482
+ await session.setName("Temporary");
483
+ await session.setName(undefined);
484
+ strictEqual(await session.getName(), undefined);
485
+ deepStrictEqual(await session.getLog(), [
486
+ { kind: "fact", seq: 1, fact: "name", name: "Temporary" },
487
+ { kind: "fact", seq: 2, fact: "name", name: undefined },
488
+ ]);
489
+ const metadata = await session.getMetadata();
490
+ const reopened = await repository.open(metadata);
491
+ strictEqual(await reopened.getName(), undefined);
492
+ deepStrictEqual(await reopened.getLog(), [
493
+ { kind: "fact", seq: 1, fact: "name", name: "Temporary" },
494
+ { kind: "fact", seq: 2, fact: "name", name: undefined },
495
+ ]);
496
+ const fork = await repository.fork(metadata, { id: "fork" });
497
+ strictEqual(await fork.getName(), undefined);
498
+ }),
480
499
  createCase(factory, "validation and immutability", "returns immutable copies from reads", async (repository) => {
481
500
  const session = await repository.create({ id: "immutable" });
482
501
  const metadata = await session.getMetadata();