@astralyn/pi 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 (163) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/cli/args.js +1 -1
  3. package/dist/cli/args.js.map +1 -1
  4. package/dist/cli/config-selector.d.ts.map +1 -1
  5. package/dist/cli/config-selector.js +2 -3
  6. package/dist/cli/config-selector.js.map +1 -1
  7. package/dist/cli/startup-ui.d.ts.map +1 -1
  8. package/dist/cli/startup-ui.js +3 -5
  9. package/dist/cli/startup-ui.js.map +1 -1
  10. package/dist/config.d.ts +5 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +8 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session.d.ts +1 -25
  15. package/dist/core/agent-session.d.ts.map +1 -1
  16. package/dist/core/agent-session.js +29 -408
  17. package/dist/core/agent-session.js.map +1 -1
  18. package/dist/core/compaction-controller.d.ts +89 -0
  19. package/dist/core/compaction-controller.d.ts.map +1 -0
  20. package/dist/core/compaction-controller.js +428 -0
  21. package/dist/core/compaction-controller.js.map +1 -0
  22. package/dist/core/extensions/index.d.ts +1 -1
  23. package/dist/core/extensions/index.d.ts.map +1 -1
  24. package/dist/core/extensions/index.js.map +1 -1
  25. package/dist/core/extensions/loader.js +3 -3
  26. package/dist/core/extensions/loader.js.map +1 -1
  27. package/dist/core/extensions/types.d.ts +1 -15
  28. package/dist/core/extensions/types.d.ts.map +1 -1
  29. package/dist/core/extensions/types.js.map +1 -1
  30. package/dist/core/provider-attribution.js +2 -2
  31. package/dist/core/provider-attribution.js.map +1 -1
  32. package/dist/core/settings-manager.js +1 -1
  33. package/dist/core/settings-manager.js.map +1 -1
  34. package/dist/core/telemetry.d.ts +1 -1
  35. package/dist/core/telemetry.d.ts.map +1 -1
  36. package/dist/core/telemetry.js +1 -1
  37. package/dist/core/telemetry.js.map +1 -1
  38. package/dist/extensions/deepwiki/index.d.ts.map +1 -1
  39. package/dist/extensions/deepwiki/index.js +23 -1
  40. package/dist/extensions/deepwiki/index.js.map +1 -1
  41. package/dist/extensions/deepwiki/render.d.ts +1 -1
  42. package/dist/extensions/deepwiki/render.d.ts.map +1 -1
  43. package/dist/extensions/deepwiki/render.js +4 -2
  44. package/dist/extensions/deepwiki/render.js.map +1 -1
  45. package/dist/extensions/index.d.ts.map +1 -1
  46. package/dist/extensions/index.js +0 -4
  47. package/dist/extensions/index.js.map +1 -1
  48. package/dist/extensions/question/index.d.ts.map +1 -1
  49. package/dist/extensions/question/index.js +0 -1
  50. package/dist/extensions/question/index.js.map +1 -1
  51. package/dist/extensions/subagent/index.d.ts.map +1 -1
  52. package/dist/extensions/subagent/index.js +20 -2
  53. package/dist/extensions/subagent/index.js.map +1 -1
  54. package/dist/extensions/todo/schema.d.ts +1 -1
  55. package/dist/extensions/todo/state.js +5 -5
  56. package/dist/extensions/todo/state.js.map +1 -1
  57. package/dist/modes/interactive/components/extension-selector.d.ts +0 -2
  58. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/extension-selector.js +1 -1
  60. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/settings-selector.js +1 -1
  62. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  63. package/dist/modes/interactive/components/tool-chrome.d.ts +28 -0
  64. package/dist/modes/interactive/components/tool-chrome.d.ts.map +1 -0
  65. package/dist/modes/interactive/components/tool-chrome.js +81 -0
  66. package/dist/modes/interactive/components/tool-chrome.js.map +1 -0
  67. package/dist/modes/interactive/components/tool-execution.d.ts +0 -3
  68. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/tool-execution.js +11 -108
  70. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  71. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  72. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  73. package/dist/modes/interactive/interactive-mode.js +35 -52
  74. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  75. package/docs/bundled/README.md +0 -2
  76. package/docs/bundled/extensions/subagent.md +1 -1
  77. package/docs/bundled/tool-presentation.md +2 -2
  78. package/docs/docs.json +0 -8
  79. package/docs/extensions.md +6 -23
  80. package/docs/settings.md +1 -1
  81. package/docs/usage.md +1 -2
  82. package/examples/extensions/README.md +2 -0
  83. package/examples/extensions/plan-mode/README.md +66 -0
  84. package/examples/extensions/plan-mode/index.ts +390 -0
  85. package/examples/extensions/plan-mode/utils.ts +168 -0
  86. package/examples/extensions/subagent/README.md +175 -0
  87. package/examples/extensions/subagent/agents/planner.md +37 -0
  88. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  89. package/examples/extensions/subagent/agents/scout.md +50 -0
  90. package/examples/extensions/subagent/agents/worker.md +24 -0
  91. package/examples/extensions/subagent/agents.ts +126 -0
  92. package/examples/extensions/subagent/index.ts +1009 -0
  93. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  94. package/examples/extensions/subagent/prompts/implement.md +10 -0
  95. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  96. package/npm-shrinkwrap.json +2 -2
  97. package/package.json +1 -1
  98. package/dist/extensions/biu/index.d.ts +0 -19
  99. package/dist/extensions/biu/index.d.ts.map +0 -1
  100. package/dist/extensions/biu/index.js +0 -350
  101. package/dist/extensions/biu/index.js.map +0 -1
  102. package/dist/extensions/biu/prompts.d.ts +0 -26
  103. package/dist/extensions/biu/prompts.d.ts.map +0 -1
  104. package/dist/extensions/biu/prompts.js +0 -260
  105. package/dist/extensions/biu/prompts.js.map +0 -1
  106. package/dist/extensions/biu/workspace.d.ts +0 -122
  107. package/dist/extensions/biu/workspace.d.ts.map +0 -1
  108. package/dist/extensions/biu/workspace.js +0 -404
  109. package/dist/extensions/biu/workspace.js.map +0 -1
  110. package/dist/extensions/rewind/anchor.d.ts +0 -29
  111. package/dist/extensions/rewind/anchor.d.ts.map +0 -1
  112. package/dist/extensions/rewind/anchor.js +0 -35
  113. package/dist/extensions/rewind/anchor.js.map +0 -1
  114. package/dist/extensions/rewind/config.d.ts +0 -16
  115. package/dist/extensions/rewind/config.d.ts.map +0 -1
  116. package/dist/extensions/rewind/config.js +0 -69
  117. package/dist/extensions/rewind/config.js.map +0 -1
  118. package/dist/extensions/rewind/engine.d.ts +0 -93
  119. package/dist/extensions/rewind/engine.d.ts.map +0 -1
  120. package/dist/extensions/rewind/engine.js +0 -778
  121. package/dist/extensions/rewind/engine.js.map +0 -1
  122. package/dist/extensions/rewind/gc.d.ts +0 -35
  123. package/dist/extensions/rewind/gc.d.ts.map +0 -1
  124. package/dist/extensions/rewind/gc.js +0 -169
  125. package/dist/extensions/rewind/gc.js.map +0 -1
  126. package/dist/extensions/rewind/index.d.ts +0 -3
  127. package/dist/extensions/rewind/index.d.ts.map +0 -1
  128. package/dist/extensions/rewind/index.js +0 -287
  129. package/dist/extensions/rewind/index.js.map +0 -1
  130. package/dist/extensions/rewind/menu.d.ts +0 -13
  131. package/dist/extensions/rewind/menu.d.ts.map +0 -1
  132. package/dist/extensions/rewind/menu.js +0 -123
  133. package/dist/extensions/rewind/menu.js.map +0 -1
  134. package/dist/extensions/rewind/paths.d.ts +0 -4
  135. package/dist/extensions/rewind/paths.d.ts.map +0 -1
  136. package/dist/extensions/rewind/paths.js +0 -15
  137. package/dist/extensions/rewind/paths.js.map +0 -1
  138. package/dist/extensions/rewind/restore.d.ts +0 -54
  139. package/dist/extensions/rewind/restore.d.ts.map +0 -1
  140. package/dist/extensions/rewind/restore.js +0 -94
  141. package/dist/extensions/rewind/restore.js.map +0 -1
  142. package/dist/extensions/rewind/snapshot.d.ts +0 -45
  143. package/dist/extensions/rewind/snapshot.d.ts.map +0 -1
  144. package/dist/extensions/rewind/snapshot.js +0 -21
  145. package/dist/extensions/rewind/snapshot.js.map +0 -1
  146. package/dist/extensions/rewind/storage.d.ts +0 -12
  147. package/dist/extensions/rewind/storage.d.ts.map +0 -1
  148. package/dist/extensions/rewind/storage.js +0 -33
  149. package/dist/extensions/rewind/storage.js.map +0 -1
  150. package/dist/extensions/rewind/text.d.ts +0 -7
  151. package/dist/extensions/rewind/text.d.ts.map +0 -1
  152. package/dist/extensions/rewind/text.js +0 -19
  153. package/dist/extensions/rewind/text.js.map +0 -1
  154. package/dist/extensions/rewind/tool-path.d.ts +0 -3
  155. package/dist/extensions/rewind/tool-path.d.ts.map +0 -1
  156. package/dist/extensions/rewind/tool-path.js +0 -9
  157. package/dist/extensions/rewind/tool-path.js.map +0 -1
  158. package/dist/utils/coalescing-terminal.d.ts +0 -132
  159. package/dist/utils/coalescing-terminal.d.ts.map +0 -1
  160. package/dist/utils/coalescing-terminal.js +0 -292
  161. package/dist/utils/coalescing-terminal.js.map +0 -1
  162. package/docs/bundled/extensions/biu.md +0 -63
  163. package/docs/bundled/extensions/rewind.md +0 -86
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/extensions/rewind/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAc,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnF,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,0EAA0E;AAC1E,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,uEAAuE;AACvE,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC3C,0FAA0F;AAC1F,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,4EAA4E;AAC5E,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,2FAA2F;AAC3F,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAStC,SAAS,aAAa,CAAC,EAAS;IAC/B,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,QAAQ,CAAC,CAAY,EAAE,CAAQ;IACvC,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AA+BD,8EAA8E;AAE9E,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;AACnD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEvC,SAAS,UAAU;IAClB,OAAO;QACN,SAAS,EAAE,EAAE;QACb,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,gBAAgB,EAAE,IAAI,GAAG,EAAE;QAC3B,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,CAAC;KACN,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,OAAO,CAAO,KAAmB,EAAE,WAAmB,EAAE,EAA2B;IACjG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,KAAK,CAAI,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,UAAU,MAAM;QACpB,SAAS,CAAC;YACT,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM;gBAAE,OAAO;YAC9B,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;QAClC,CAAC;IACF,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7D,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,yFAAyF;AACzF,SAAS,kBAAkB,CAAC,SAAgC;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC1E,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9B,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC5B,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,CAAC,CAAC,EAAE,CAAC;QACR,CAAC,GAAG,UAAU,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,CAAC,CAAC;AACV,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,GAAW;IACvD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACnB,QAAQ,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACzC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACvC,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,MAAM,CAAC,GAAW;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AACvC,CAAC;AAED,8EAA8E;AAE9E,yFAAyF;AACzF,SAAS,OAAO,CAAC,OAAe,EAAE,GAAW;IAC5C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,OAAO,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACpE,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,SAAS,MAAM,CAAC,QAAgB,EAAE,GAAW;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB,EAAE,OAAe;IACpD,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3E,OAAO,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,IAAY;IAC/C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,cAAc,CAAC,KAAuB,EAAE,QAAgB;IAChE,OAAO,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED,yFAAyF;AACzF,SAAS,eAAe,CAAC,KAAuB,EAAE,QAAgB;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,UAAU,CAAC;IAC/C,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,sFAAsF;AACtF,SAAS,mBAAmB,CAC3B,KAAuB,EACvB,MAA2B,EAC3B,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IAC7B,OAAO,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED,uEAAuE;AACvE,gFAAgF;AAEhF,SAAS,QAAQ,CAAC,CAAU;IAC3B,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAK,CAAuB,CAAC,IAAI,KAAK,QAAQ,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC;QACJ,OAAO,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,CAAC,CAAC;IACT,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,iBAAiB,CAAC,KAAa,EAAE,KAAa,EAAE,IAAY;IAC1E,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACnF,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;QACrD,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;YACtD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;aACjC,CAAC,CAAC;YACH,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YACtD,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC;gBAAE,OAAO,SAAS,KAAK,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC/F,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;QAC9B,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC;AACF,CAAC;AAED,+DAA+D;AAC/D,KAAK,UAAU,aAAa,CAC3B,GAAW,EACX,QAAgB,EAChB,IAAY,EACZ,IAA6C;IAE7C,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAE5D,sCAAsC;IACtC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACpE,6EAA6E;IAC7E,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAClE,+EAA+E;IAC/E,sFAAsF;IACtF,IAAI,IAAI,CAAC,IAAI,GAAG,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,CAAC;QACJ,OAAO,CAAC,CAAC,MAAM,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,8EAA8E;AAE9E,oFAAoF;AACpF,KAAK,UAAU,YAAY,CAC1B,GAAW,EACX,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,KAAoB;IAEpB,IAAI,GAAU,CAAC;IACf,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACX,GAAG,GAAG,KAAK,CAAC;IACb,CAAC;SAAM,CAAC;QACP,IAAI,CAAC;YACJ,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,IAAI,QAAQ,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACtD,MAAM,CAAC,CAAC;QACT,CAAC;IACF,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,sDAAsD;IACvD,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,gBAAgB,CACxB,GAAW,EACX,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,KAAoB;IAEpB,IAAI,GAAU,CAAC;IACf,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACX,GAAG,GAAG,KAAK,CAAC;IACb,CAAC;SAAM,CAAC;QACP,IAAI,CAAC;YACJ,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,IAAI,QAAQ,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACtD,MAAM,CAAC,CAAC;QACT,CAAC;IACF,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACJ,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,CAAC;QAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC;QACJ,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACR,cAAc;IACf,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,QAAgB,EAAE,IAAY;IACvE,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,IAAW,CAAC;IAChB,IAAI,CAAC;QACJ,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,CAAC,4CAA4C;IACrD,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,cAAc;IACf,CAAC;AACF,CAAC;AAED,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,OAAO,GAA+B,EAAE,CAAC;IAC/C,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAChF,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC/C,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACT,wEAAwE;gBACxE,oEAAoE;gBACpE,sEAAsE;gBACtE,wDAAwD;gBACxD,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAChC,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;oBAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;oBAC3B,OAAO;gBACR,CAAC;gBACD,MAAM,MAAM,GAAe,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;gBACtE,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;gBAC3B,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC7C,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO;YACR,CAAC;YACD,wEAAwE;YACxE,gEAAgE;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;gBACxE,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;gBAC3B,OAAO;YACR,CAAC;YACD,IACC,MAAM;gBACN,MAAM,CAAC,UAAU,KAAK,IAAI;gBAC1B,CAAC,CAAC,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EACvF,CAAC;gBACF,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC,qBAAqB;gBACjD,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChD,OAAO;YACR,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;YAC7E,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;YAC5B,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9C,yEAAyE;YACzE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;YAChD,KAAK,GAAG,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACR,uCAAuC;QACxC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC9G,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,KAAuB,EAAE,QAAgB;IACnE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IACtF,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QAC3C,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,OAAO,QAAQ,CAAC;IACvD,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,OAAe;IACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAEjE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1G,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,6BAA6B;IAErF,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,8EAA8E;IAC9E,IAAI,OAAO,GAAiB,IAAI,CAAC;IACjC,IAAI,CAAC;QACJ,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3E,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;IACpD,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,OAAO;QAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;;QAC7D,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CACtB,GAAW,EACX,WAAmB,EACnB,MAAc,EACd,MAAc,EACd,SAAiB,EACjB,YAAY,GAAG,aAAa;IAE5B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC9B,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACpB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,0EAA0E;IAC1E,4EAA4E;IAC5E,8EAA8E;IAC9E,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAwB,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC1F,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;QAChF,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;QACvD,KAAK,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IACD,KAAK,CAAC,GAAG,EAAE,CAAC;IACZ,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,iBAAiB,CAC/B,GAAW,EACX,OAA8B,EAC9B,QAA+B;IAE/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,CAAC,UAAU;gBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC;IACF,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvE,CAAC;IACF,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO;IAC9B,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,MAA2B;IAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC3E,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACvC,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC1D,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACpC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,CAAC;YACD,OAAO,CAAC,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACvD,CAAC;AAcD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,GAAW,EACX,MAA2B,EAC3B,YAA+B;IAE/B,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,iEAAiE;IACjE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QAC3C,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,CAAC,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QACnF,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7C,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC1D,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;YAC/D,OAAO,MAAM,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC;QAC3B,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,eAAe,CAC7B,GAAW,EACX,QAAgB,EAChB,UAAyB;IAEzB,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACnE,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACzB,qDAAqD;QACrD,IAAI,WAAW,KAAK,IAAI;YAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IAC9D,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;IACxF,IAAI,WAAW,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACxF,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IAC5F,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IACtF,OAAO,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,iEAAiE;AACjE,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IAC/D,IAAI,CAAC;QACJ,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACrB,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,6CAA6C;QAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,QAAQ;YAAE,CAAC,EAAE,CAAC;IAC7C,CAAC;IACD,0EAA0E;IAC1E,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,CAAC,EAAE,CAAC;IAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,EAAU;IAChD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACjE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC;YAAE,UAAU,IAAI,CAAC,GAAG,CAAC,CAAC;aAC1B,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IAClC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,2EAA2E;IAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IACpE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,GAAW,EACX,MAA2B,EAC3B,IAA2B;IAE3B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,EAAE,SAAS,CAAC;IAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE;QACpE,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC3E,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACvC,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC1D,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC,CAAC,oCAAoC;YACzE,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBACnB,IAAI,CAAC;oBACJ,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACvB,oDAAoD;oBACpD,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAChC,OAAO,QAAQ,CAAC;gBACjB,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACZ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;wBAAE,MAAM,CAAC,CAAC;oBAC1B,OAAO,IAAI,CAAC;gBACb,CAAC;YACF,CAAC;YACD,wEAAwE;YACxE,IAAI,IAAI,IAAI,CAAC,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBACxD,MAAM,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;gBACzC,qEAAqE;gBACrE,qDAAqD;gBACrD,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAChC,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACvD,CAAC;AAED,8EAA8E;AAE9E,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,SAAgC,EAAE,YAAY,GAAG,aAAa;IAC1F,OAAO,SAAS,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrF,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,yBAAyB,CACxC,GAAW,EACX,GAAW,EACX,SAAgC,EAChC,YAAY,GAAG,aAAa;IAE5B,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACnB,6EAA6E;IAC7E,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;QACxC,KAAK,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAAE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;QACf,SAAS,EAAE,CAAC,GAAG,QAAQ,CAAC;QACxB,YAAY;QACZ,gBAAgB,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QAC9C,0EAA0E;QAC1E,8CAA8C;QAC9C,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,KAAK;QACZ,GAAG,EAAE,QAAQ,CAAC,MAAM;KACpB,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC9C,OAAe,EACf,GAAW,EACX,SAAgC;IAEhC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO;IACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,CAAC,UAAU,CACvB,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;SACpC,MAAM,CAAC,CAAC,CAAC,EAA4C,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC;SAC9E,GAAG,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,MAAM,IAAI,GAAI,CAAuB,CAAC,IAAI,CAAC;YAC3C,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO;YAC9B,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACR,oEAAoE;YACrE,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CACH,CACD,CAAC;AACH,CAAC","sourcesContent":["/**\n * engine.ts — file-history backup engine for the rewind extension.\n *\n * Ported from Claude Code 2.1.88's src/utils/fileHistory.ts, adapted from its\n * React state-updater to module-level per-session maps. The core idea (and the\n * reason this exists): we back up ONLY the files Pi's edit/write tools are about\n * to modify — one copyFile before the edit — instead of snapshotting the whole\n * work tree. Cost is proportional to \"how many files Pi changed\", not project\n * size, so it never blocks the session-lifecycle critical path and storage stays\n * tiny.\n *\n * Per turn:\n * - beginTurn() (before_agent_start): open a working frame and re-record every\n * already-tracked file at its current (turn-start) state, reusing the latest\n * backup when unchanged (worktree lastSeen → mtime/size/content), creating a\n * new version when not. lastSeen skips backup-blob IO when the worktree has\n * not moved since the last confirm; content compare streams in 64 KiB chunks.\n * - trackEdit() (tool_call edit|write, before the write): back up a *newly*\n * edited file at its pre-edit state into the working frame (null marker when\n * the target does not exist yet, so rewind deletes the created file).\n * - endTurn() (agent_settled): if anything changed AND the caller resolved a\n * turn anchor, stamp + return the frame to persist; else discard it (an\n * unanchored frame could never be matched by /tree and would exist only in\n * memory). agent_settled (not agent_end) so auto-retry / overflow\n * compact-retry / queued follow-ups share one logical turn.\n *\n * Rewind = applySnapshot(): restore every tracked file to the version recorded in\n * the target frame (copy back / delete for null), touching only files that differ.\n *\n * Backups live at <rewindBackupsDir(sessionId)>/<sha256(relpath)[:16]>@v<n>.\n */\nimport { createHash } from \"node:crypto\";\nimport { chmodSync, copyFileSync, mkdirSync, type Stats, statSync } from \"node:fs\";\nimport { chmod, copyFile, link, mkdir, open, readFile, stat, unlink } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative } from \"node:path\";\nimport type { FileBackup, FileHistorySnapshot } from \"./snapshot.ts\";\nimport { backupsDir } from \"./storage.ts\";\n\n/** Cap on retained snapshots per session (matches CC's MAX_SNAPSHOTS). */\nconst MAX_SNAPSHOTS = 100;\n/** Avoid reading very large files solely to compare backup content. */\nconst MAX_CONTENT_BYTES = 25 * 1024 * 1024;\n/** Cap concurrent backup/compare/restore IO so long tracked sets don't thrash handles. */\nconst IO_CONCURRENCY = 16;\n/** Cap bytes loaded for coarse restore line stats (confirm dialog only). */\nconst MAX_DIFF_BYTES = 1_048_576;\n/** Streaming compare chunk size (avoids buffering whole files up to MAX_CONTENT_BYTES). */\nconst COMPARE_CHUNK_BYTES = 64 * 1024;\n\n/** Worktree identity recorded when we last backed up / confirmed a tracking path. */\ninterface SeenStats {\n\tmtimeMs: number;\n\tsize: number;\n\tmode: number;\n}\n\nfunction seenFromStats(st: Stats): SeenStats {\n\treturn { mtimeMs: st.mtimeMs, size: st.size, mode: st.mode };\n}\n\nfunction sameSeen(a: SeenStats, b: Stats): boolean {\n\treturn a.mtimeMs === b.mtimeMs && a.size === b.size && a.mode === b.mode;\n}\n\nexport interface FileHistoryState {\n\t/** Finalized + persisted frames, oldest first. */\n\tsnapshots: FileHistorySnapshot[];\n\t/** All tracking-paths ever edited this session. */\n\ttrackedFiles: Set<string>;\n\t/** Latest backup per tracking path (finalized + in-progress pending versions). */\n\tlatestByTracking: Map<string, FileBackup>;\n\t/**\n\t * Worktree mtime/size/mode at the last backup or unchanged confirmation.\n\t * beginTurn uses this to skip backup-blob stats/content when the worktree\n\t * has not moved since we last recorded it (still stats the worktree once).\n\t */\n\tlastSeen: Map<string, SeenStats>;\n\t/** The current turn's working frame (built across the turn, persisted at endTurn). */\n\tpending: FileHistorySnapshot | null;\n\t/** Whether the pending frame differs from the last finalized frame. */\n\tdirty: boolean;\n\t/** Monotonic activity counter (incremented on every finalized frame). */\n\tseq: number;\n}\n\nexport interface ApplySnapshotOptions {\n\t/**\n\t * Absolute paths already known to differ (e.g. from collectChanges).\n\t * When set, only those paths are restored/deleted and originChanged is skipped.\n\t */\n\tonlyPaths?: ReadonlySet<string>;\n}\n\n// ---- per-session state ----------------------------------------------------\n\nconst states = new Map<string, FileHistoryState>();\nconst cwds = new Map<string, string>();\n\nfunction freshState(): FileHistoryState {\n\treturn {\n\t\tsnapshots: [],\n\t\ttrackedFiles: new Set(),\n\t\tlatestByTracking: new Map(),\n\t\tlastSeen: new Map(),\n\t\tpending: null,\n\t\tdirty: false,\n\t\tseq: 0,\n\t};\n}\n\n/** Run async work over items with a fixed concurrency cap. Preserves result order. */\nasync function mapPool<T, R>(items: readonly T[], concurrency: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n\tif (items.length === 0) return [];\n\tconst results = new Array<R>(items.length);\n\tlet next = 0;\n\tasync function worker(): Promise<void> {\n\t\tfor (;;) {\n\t\t\tconst i = next++;\n\t\t\tif (i >= items.length) return;\n\t\t\tresults[i] = await fn(items[i]!);\n\t\t}\n\t}\n\tconst n = Math.min(concurrency, items.length);\n\tawait Promise.all(Array.from({ length: n }, () => worker()));\n\treturn results;\n}\n\n/** Rebuild latestByTracking from finalized frames (oldest → newest, last write wins). */\nfunction rebuildLatestIndex(snapshots: FileHistorySnapshot[]): Map<string, FileBackup> {\n\tconst latest = new Map<string, FileBackup>();\n\tfor (const snap of snapshots) {\n\t\tfor (const [tracking, backup] of Object.entries(snap.trackedFileBackups)) {\n\t\t\tlatest.set(tracking, backup);\n\t\t}\n\t}\n\treturn latest;\n}\n\nfunction getState(sid: string): FileHistoryState {\n\tlet s = states.get(sid);\n\tif (!s) {\n\t\ts = freshState();\n\t\tstates.set(sid, s);\n\t}\n\treturn s;\n}\n\n/** Bind a session id to its cwd (for relative-path keying). Call at session_start. */\nexport function registerSession(sid: string, cwd: string): void {\n\tcwds.set(sid, cwd);\n\tgetState(sid);\n}\n\nexport function disposeSession(sid: string): void {\n\tstates.delete(sid);\n\tcwds.delete(sid);\n}\n\nexport function getSnapshots(sid: string): FileHistorySnapshot[] {\n\treturn states.get(sid)?.snapshots ?? [];\n}\n\nfunction cwdFor(sid: string): string {\n\treturn cwds.get(sid) ?? process.cwd();\n}\n\n// ---- path helpers ---------------------------------------------------------\n\n/** Use the cwd-relative path as the tracking key when inside cwd (shorter, portable). */\nfunction shorten(absPath: string, cwd: string): string {\n\tif (!isAbsolute(absPath)) return absPath;\n\tif (absPath === cwd) return absPath;\n\tconst rel = relative(cwd, absPath);\n\tif (!rel || rel.startsWith(\"..\") || isAbsolute(rel)) return absPath;\n\treturn rel;\n}\n\nfunction expand(tracking: string, cwd: string): string {\n\treturn isAbsolute(tracking) ? tracking : join(cwd, tracking);\n}\n\nfunction backupName(tracking: string, version: number): string {\n\tconst h = createHash(\"sha256\").update(tracking).digest(\"hex\").slice(0, 16);\n\treturn `${h}@v${version}`;\n}\n\nfunction backupPathFor(sid: string, name: string): string {\n\treturn join(backupsDir(sid), name);\n}\n\nfunction latestBackupOf(state: FileHistoryState, tracking: string): FileBackup | undefined {\n\treturn state.latestByTracking.get(tracking);\n}\n\n/** First-ever recorded backup for a file (used when rewinding before it was tracked). */\nfunction firstBackupName(state: FileHistoryState, tracking: string): string | null | undefined {\n\tfor (const snap of state.snapshots) {\n\t\tconst b = snap.trackedFileBackups[tracking];\n\t\tif (b && b.version === 1) return b.backupName;\n\t}\n\treturn undefined;\n}\n\n/** Resolve the backup blob name for `tracking` at `target` (null = did not exist). */\nfunction backupNameForTarget(\n\tstate: FileHistoryState,\n\ttarget: FileHistorySnapshot,\n\ttracking: string,\n): string | null | undefined {\n\tconst tb = target.trackedFileBackups[tracking];\n\tif (tb) return tb.backupName;\n\treturn firstBackupName(state, tracking);\n}\n\n// ---- change detection (ported fileHistory.ts compareStatsAndContent;\n// content path streams via filesEqualChunked instead of full-buffer reads)\n\nfunction isENOENT(e: unknown): boolean {\n\treturn !!e && typeof e === \"object\" && (e as { code?: string }).code === \"ENOENT\";\n}\n\nasync function statOrNull(p: string): Promise<Stats | null> {\n\ttry {\n\t\treturn await stat(p);\n\t} catch (e) {\n\t\tif (isENOENT(e)) return null;\n\t\tthrow e;\n\t}\n}\n\n/**\n * Raw-byte equality without buffering whole files. A utf-8 decode maps invalid\n * sequences to U+FFFD, which can make two DIFFERENT binary files compare equal —\n * and then rewind would silently skip both the re-backup and the restore.\n */\nasync function filesEqualChunked(aPath: string, bPath: string, size: number): Promise<boolean> {\n\tif (size === 0) return true;\n\tconst [aHandle, bHandle] = await Promise.all([open(aPath, \"r\"), open(bPath, \"r\")]);\n\ttry {\n\t\tconst aBuf = Buffer.allocUnsafe(COMPARE_CHUNK_BYTES);\n\t\tconst bBuf = Buffer.allocUnsafe(COMPARE_CHUNK_BYTES);\n\t\tlet remaining = size;\n\t\twhile (remaining > 0) {\n\t\t\tconst want = Math.min(COMPARE_CHUNK_BYTES, remaining);\n\t\t\tconst [aRead, bRead] = await Promise.all([\n\t\t\t\taHandle.read(aBuf, 0, want, null),\n\t\t\t\tbHandle.read(bBuf, 0, want, null),\n\t\t\t]);\n\t\t\tif (aRead.bytesRead !== bRead.bytesRead) return false;\n\t\t\tif (aRead.bytesRead === 0) return remaining === 0;\n\t\t\tif (!aBuf.subarray(0, aRead.bytesRead).equals(bBuf.subarray(0, bRead.bytesRead))) return false;\n\t\t\tremaining -= aRead.bytesRead;\n\t\t}\n\t\treturn true;\n\t} finally {\n\t\tawait Promise.allSettled([aHandle.close(), bHandle.close()]);\n\t}\n}\n\n/** True when the on-disk file differs from its backup blob. */\nasync function originChanged(\n\tsid: string,\n\tfilePath: string,\n\tname: string,\n\topts?: { hint?: Stats; trustMtime?: boolean },\n): Promise<boolean> {\n\tconst backupPath = backupPathFor(sid, name);\n\tconst orig = opts?.hint ?? (await statOrNull(filePath).catch(() => null));\n\tconst back = await statOrNull(backupPath).catch(() => null);\n\n\t// One exists, one missing -> changed.\n\tif ((orig === null) !== (back === null)) return true;\n\tif (orig === null || back === null) return false;\n\tif (orig.mode !== back.mode || orig.size !== back.size) return true;\n\t// Original untouched since the backup was written -> unchanged (skip content\n\t// read). Trusted only on the beginTurn path (worst case there: one redundant\n\t// version). Restore-side change detection must not be fooled by a same-size\n\t// content swap that carries an older mtime (archive extraction, touch -d).\n\tif (opts?.trustMtime && orig.mtimeMs < back.mtimeMs) return false;\n\t// Memory guard: don't load huge files just to byte-compare; assume changed and\n\t// let createBackup stream a new version instead (copyFile never buffers whole files).\n\tif (orig.size > MAX_CONTENT_BYTES) return true;\n\ttry {\n\t\treturn !(await filesEqualChunked(filePath, backupPath, orig.size));\n\t} catch {\n\t\treturn true;\n\t}\n}\n\n// ---- backup / restore IO --------------------------------------------------\n\n/** Copy the file's current contents into a backup blob; null backup when absent. */\nasync function createBackup(\n\tsid: string,\n\tfilePath: string,\n\ttracking: string,\n\tversion: number,\n\tknown?: Stats | null,\n): Promise<FileBackup> {\n\tlet src: Stats;\n\tif (known === null) return { backupName: null, version };\n\tif (known) {\n\t\tsrc = known;\n\t} else {\n\t\ttry {\n\t\t\tsrc = await stat(filePath);\n\t\t} catch (e) {\n\t\t\tif (isENOENT(e)) return { backupName: null, version };\n\t\t\tthrow e;\n\t\t}\n\t}\n\tconst name = backupName(tracking, version);\n\tconst dest = backupPathFor(sid, name);\n\ttry {\n\t\tawait copyFile(filePath, dest);\n\t} catch (e) {\n\t\tif (!isENOENT(e)) throw e;\n\t\tawait mkdir(dirname(dest), { recursive: true });\n\t\tawait copyFile(filePath, dest);\n\t}\n\ttry {\n\t\tawait chmod(dest, src.mode);\n\t} catch {\n\t\t// best-effort (e.g. Windows); content is what matters\n\t}\n\treturn { backupName: name, version };\n}\n\n/**\n * Synchronous backup, used on the edit critical path (tool_call). Doing the copy\n * synchronously guarantees it completes before the hook returns control to the\n * host — so the backup always captures the file's PRE-edit contents, regardless\n * of whether the host awaits the hook before running the tool.\n *\n * `known` reuses a pre-stat from the caller (`null` = known ENOENT) so trackEdit\n * can record lastSeen without a second statSync on the critical path.\n */\nfunction createBackupSync(\n\tsid: string,\n\tfilePath: string,\n\ttracking: string,\n\tversion: number,\n\tknown?: Stats | null,\n): FileBackup {\n\tlet src: Stats;\n\tif (known === null) return { backupName: null, version };\n\tif (known) {\n\t\tsrc = known;\n\t} else {\n\t\ttry {\n\t\t\tsrc = statSync(filePath);\n\t\t} catch (e) {\n\t\t\tif (isENOENT(e)) return { backupName: null, version };\n\t\t\tthrow e;\n\t\t}\n\t}\n\tconst name = backupName(tracking, version);\n\tconst dest = backupPathFor(sid, name);\n\ttry {\n\t\tcopyFileSync(filePath, dest);\n\t} catch (e) {\n\t\tif (!isENOENT(e)) throw e;\n\t\tmkdirSync(dirname(dest), { recursive: true });\n\t\tcopyFileSync(filePath, dest);\n\t}\n\ttry {\n\t\tchmodSync(dest, src.mode);\n\t} catch {\n\t\t// best-effort\n\t}\n\treturn { backupName: name, version };\n}\n\nasync function restoreBackup(sid: string, filePath: string, name: string): Promise<void> {\n\tconst backupPath = backupPathFor(sid, name);\n\tlet back: Stats;\n\ttry {\n\t\tback = await stat(backupPath);\n\t} catch {\n\t\treturn; // backup vanished; leave the file untouched\n\t}\n\ttry {\n\t\tawait copyFile(backupPath, filePath);\n\t} catch (e) {\n\t\tif (!isENOENT(e)) throw e;\n\t\tawait mkdir(dirname(filePath), { recursive: true });\n\t\tawait copyFile(backupPath, filePath);\n\t}\n\ttry {\n\t\tawait chmod(filePath, back.mode);\n\t} catch {\n\t\t// best-effort\n\t}\n}\n\n// ---- per-turn lifecycle ---------------------------------------------------\n\n/**\n * Open the working frame for a turn and re-record every tracked file at its\n * current state (reuse latest backup when unchanged, new version when changed).\n * Sets state.dirty when any file produced a new version.\n */\nexport async function beginTurn(sid: string): Promise<void> {\n\tconst state = getState(sid);\n\tconst cwd = cwdFor(sid);\n\tconst backups: Record<string, FileBackup> = {};\n\tlet dirty = false;\n\n\tawait mapPool(Array.from(state.trackedFiles), IO_CONCURRENCY, async (tracking) => {\n\t\ttry {\n\t\t\tconst filePath = expand(tracking, cwd);\n\t\t\tconst latest = latestBackupOf(state, tracking);\n\t\t\tconst nextVersion = latest ? latest.version + 1 : 1;\n\t\t\tconst st = await statOrNull(filePath);\n\t\t\tif (!st) {\n\t\t\t\t// Already recorded absent in the latest frame -> reuse it. Allocating a\n\t\t\t\t// fresh null version every turn would pin dirty=true forever once a\n\t\t\t\t// tracked file is deleted, defeating the \"skip unchanged turns\" check\n\t\t\t\t// and flushing real checkpoints out of the capped ring.\n\t\t\t\tstate.lastSeen.delete(tracking);\n\t\t\t\tif (latest && latest.backupName === null) {\n\t\t\t\t\tbackups[tracking] = latest;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst absent: FileBackup = { backupName: null, version: nextVersion };\n\t\t\t\tbackups[tracking] = absent;\n\t\t\t\tstate.latestByTracking.set(tracking, absent);\n\t\t\t\tdirty = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Worktree identity unchanged since last backup/confirm → reuse without\n\t\t\t// opening the backup blob (still paid one worktree stat above).\n\t\t\tconst seen = state.lastSeen.get(tracking);\n\t\t\tif (latest && latest.backupName !== null && seen && sameSeen(seen, st)) {\n\t\t\t\tbackups[tracking] = latest;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tlatest &&\n\t\t\t\tlatest.backupName !== null &&\n\t\t\t\t!(await originChanged(sid, filePath, latest.backupName, { hint: st, trustMtime: true }))\n\t\t\t) {\n\t\t\t\tbackups[tracking] = latest; // unchanged -> reuse\n\t\t\t\tstate.lastSeen.set(tracking, seenFromStats(st));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst created = await createBackup(sid, filePath, tracking, nextVersion, st);\n\t\t\tbackups[tracking] = created;\n\t\t\tstate.latestByTracking.set(tracking, created);\n\t\t\t// Record the pre-backup worktree identity (copyFile does not change it).\n\t\t\tstate.lastSeen.set(tracking, seenFromStats(st));\n\t\t\tdirty = true;\n\t\t} catch {\n\t\t\t// skip this file; never break the turn\n\t\t}\n\t});\n\n\tstate.pending = { v: 1, userEntryId: \"\", turnId: \"\", prompt: \"\", trackedFileBackups: backups, timestamp: \"\" };\n\tstate.dirty = dirty;\n}\n\n/**\n * Back up a file about to be edited/written, if not already captured this turn.\n * Call from the tool_call hook BEFORE the edit lands. ENOENT target -> null\n * marker (rewind deletes the created file). Synchronous so the backup is on disk\n * before the hook returns (see createBackupSync).\n */\n/**\n * Reuse an existing tracking key that differs only by case (Windows paths are\n * case-insensitive): two keys for one physical file would race each other in\n * applySnapshot's concurrent restore.\n */\nfunction canonicalTracking(state: FileHistoryState, tracking: string): string {\n\tif (process.platform !== \"win32\" || state.trackedFiles.has(tracking)) return tracking;\n\tconst lower = tracking.toLowerCase();\n\tfor (const existing of state.trackedFiles) {\n\t\tif (existing.toLowerCase() === lower) return existing;\n\t}\n\treturn tracking;\n}\n\nexport function trackEdit(sid: string, absPath: string): void {\n\tconst state = getState(sid);\n\tconst cwd = cwdFor(sid);\n\tconst tracking = canonicalTracking(state, shorten(absPath, cwd));\n\n\tif (!state.pending) {\n\t\tstate.pending = { v: 1, userEntryId: \"\", turnId: \"\", prompt: \"\", trackedFileBackups: {}, timestamp: \"\" };\n\t}\n\tif (state.pending.trackedFileBackups[tracking]) return; // already captured this turn\n\n\tconst latest = latestBackupOf(state, tracking);\n\tconst version = latest ? latest.version + 1 : 1;\n\tconst filePath = expand(tracking, cwd);\n\t// One pre-edit stat: drives the backup and lastSeen (null = new-file marker).\n\tlet preEdit: Stats | null = null;\n\ttry {\n\t\tpreEdit = statSync(filePath);\n\t} catch (e) {\n\t\tif (!isENOENT(e)) throw e;\n\t}\n\tconst backup = createBackupSync(sid, filePath, tracking, version, preEdit);\n\tstate.pending.trackedFileBackups[tracking] = backup;\n\tstate.trackedFiles.add(tracking);\n\tstate.latestByTracking.set(tracking, backup);\n\tif (preEdit) state.lastSeen.set(tracking, seenFromStats(preEdit));\n\telse state.lastSeen.delete(tracking);\n\tstate.dirty = true;\n}\n\n/**\n * Finalize the turn. Returns the snapshot to persist (caller appendEntry's it),\n * or null when nothing changed this turn. Pushes finalized frames into the\n * in-memory ring (capped at MAX_SNAPSHOTS).\n */\nexport function endTurn(\n\tsid: string,\n\tuserEntryId: string,\n\tturnId: string,\n\tprompt: string,\n\ttimestamp: string,\n\tmaxSnapshots = MAX_SNAPSHOTS,\n): FileHistorySnapshot | null {\n\tconst state = getState(sid);\n\tconst pending = state.pending;\n\tstate.pending = null;\n\tif (!pending || !state.dirty) {\n\t\tstate.dirty = false;\n\t\treturn null;\n\t}\n\tstate.dirty = false;\n\t// No resolvable anchor (e.g. a custom-triggered run that appended no user\n\t// entry): discard the frame. Pushing it into the ring without persisting it\n\t// would diverge memory from the JSONL, and /tree could never match it anyway.\n\tif (!userEntryId) return null;\n\tconst frame: FileHistorySnapshot = { ...pending, userEntryId, turnId, prompt, timestamp };\n\tstate.snapshots.push(frame);\n\tif (state.snapshots.length > maxSnapshots) {\n\t\tconst dropped = state.snapshots.slice(0, state.snapshots.length - maxSnapshots);\n\t\tstate.snapshots = state.snapshots.slice(-maxSnapshots);\n\t\tvoid pruneDroppedBlobs(sid, dropped, state.snapshots);\n\t}\n\tstate.seq++;\n\treturn frame;\n}\n\n/**\n * Best-effort deletion of backup blobs referenced ONLY by frames dropped from\n * the capped ring. Backups are reused across frames (an unchanged file keeps\n * pointing at the same version), so a blob is unlinked only when no retained\n * frame still references it. Without this, the cap trims the in-memory index\n * while the blob files accumulate for the whole session — gc.ts only reclaims\n * whole session directories.\n */\nasync function pruneDroppedBlobs(\n\tsid: string,\n\tdropped: FileHistorySnapshot[],\n\tretained: FileHistorySnapshot[],\n): Promise<void> {\n\tconst live = new Set<string>();\n\tfor (const snap of retained) {\n\t\tfor (const b of Object.values(snap.trackedFileBackups)) {\n\t\t\tif (b.backupName) live.add(b.backupName);\n\t\t}\n\t}\n\tconst doomed = new Set<string>();\n\tfor (const snap of dropped) {\n\t\tfor (const b of Object.values(snap.trackedFileBackups)) {\n\t\t\tif (b.backupName && !live.has(b.backupName)) doomed.add(b.backupName);\n\t\t}\n\t}\n\tif (doomed.size === 0) return;\n\tawait Promise.allSettled(Array.from(doomed, (name) => unlink(backupPathFor(sid, name))));\n}\n\n// ---- rewind ---------------------------------------------------------------\n\n/**\n * Absolute paths that restoring to `target` would change on disk (empty =\n * nothing to do). Same walk applySnapshot performs, without writing — the\n * caller can both count and preview the files from one pass. Concurrent with\n * IO_CONCURRENCY; result order matches trackedFiles insertion order.\n */\nexport async function collectChanges(sid: string, target: FileHistorySnapshot): Promise<string[]> {\n\tconst state = getState(sid);\n\tconst cwd = cwdFor(sid);\n\tconst trackings = Array.from(state.trackedFiles);\n\tconst results = await mapPool(trackings, IO_CONCURRENCY, async (tracking) => {\n\t\ttry {\n\t\t\tconst filePath = expand(tracking, cwd);\n\t\t\tconst name = backupNameForTarget(state, target, tracking);\n\t\t\tif (name === undefined) return null;\n\t\t\tif (name === null) {\n\t\t\t\treturn (await statOrNull(filePath)) ? filePath : null;\n\t\t\t}\n\t\t\treturn (await originChanged(sid, filePath, name)) ? filePath : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t});\n\treturn results.filter((p): p is string => p !== null);\n}\n\nexport interface CoarseDiffStats {\n\t/** Absolute paths that would change (same as collectChanges). */\n\tpaths: string[];\n\t/**\n\t * Coarse line insertions when restoring (lines present in backup, not in\n\t * current). Bag-of-lines, not a true Myers diff — confirm UI only.\n\t */\n\tinsertions: number;\n\t/** Coarse line deletions when restoring (lines present in current, not in backup). */\n\tdeletions: number;\n}\n\n/**\n * Coarse +N/−M line stats for a restore confirm dialog. Only loads text for\n * paths already known to differ (from collectChanges). Oversized files still\n * appear in `paths` but contribute 0 to line totals. No extra dependency.\n */\nexport async function collectChangeDiffStats(\n\tsid: string,\n\ttarget: FileHistorySnapshot,\n\tchangedPaths: readonly string[],\n): Promise<CoarseDiffStats> {\n\tif (changedPaths.length === 0) {\n\t\treturn { paths: [], insertions: 0, deletions: 0 };\n\t}\n\tconst state = getState(sid);\n\tconst cwd = cwdFor(sid);\n\t// Reverse map abs path → tracking key for the known changed set.\n\tconst trackingByAbs = new Map<string, string>();\n\tfor (const tracking of state.trackedFiles) {\n\t\ttrackingByAbs.set(expand(tracking, cwd), tracking);\n\t}\n\n\tconst results = await mapPool([...changedPaths], IO_CONCURRENCY, async (filePath) => {\n\t\ttry {\n\t\t\tconst tracking = trackingByAbs.get(filePath);\n\t\t\tif (!tracking) return { insertions: 0, deletions: 0 };\n\t\t\tconst name = backupNameForTarget(state, target, tracking);\n\t\t\tif (name === undefined) return { insertions: 0, deletions: 0 };\n\t\t\treturn await coarseLineDelta(sid, filePath, name);\n\t\t} catch {\n\t\t\treturn { insertions: 0, deletions: 0 };\n\t\t}\n\t});\n\n\tlet insertions = 0;\n\tlet deletions = 0;\n\tfor (const r of results) {\n\t\tinsertions += r.insertions;\n\t\tdeletions += r.deletions;\n\t}\n\treturn { paths: [...changedPaths], insertions, deletions };\n}\n\n/**\n * Bag-of-lines delta for current worktree → backup target (restore direction).\n * insertions = lines that appear after restore; deletions = lines removed.\n */\nasync function coarseLineDelta(\n\tsid: string,\n\tfilePath: string,\n\tbackupName: string | null,\n): Promise<{ insertions: number; deletions: number }> {\n\tconst currentText = await readTextCapped(filePath, MAX_DIFF_BYTES);\n\tif (backupName === null) {\n\t\t// Snapshot: file did not exist → restore deletes it.\n\t\tif (currentText === null) return { insertions: 0, deletions: 0 };\n\t\treturn { insertions: 0, deletions: countLines(currentText) };\n\t}\n\tconst backupText = await readTextCapped(backupPathFor(sid, backupName), MAX_DIFF_BYTES);\n\tif (currentText === null && backupText === null) return { insertions: 0, deletions: 0 };\n\tif (currentText === null) return { insertions: countLines(backupText ?? \"\"), deletions: 0 };\n\tif (backupText === null) return { insertions: 0, deletions: countLines(currentText) };\n\treturn bagOfLinesDelta(currentText, backupText);\n}\n\n/** null = missing or oversize/unreadable (skip line contrib). */\nasync function readTextCapped(filePath: string, maxBytes: number): Promise<string | null> {\n\ttry {\n\t\tconst st = await statOrNull(filePath);\n\t\tif (!st) return null;\n\t\tif (st.size > maxBytes) return null;\n\t\tconst buf = await readFile(filePath);\n\t\t// Skip obvious binary (NUL in the first 8K).\n\t\tconst sample = buf.subarray(0, Math.min(buf.length, 8192));\n\t\tif (sample.includes(0)) return null;\n\t\treturn buf.toString(\"utf8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction countLines(text: string): number {\n\tif (text.length === 0) return 0;\n\tlet n = 1;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tif (text.charCodeAt(i) === 10 /* \\n */) n++;\n\t}\n\t// Trailing newline does not add an extra empty \"line\" for display counts.\n\tif (text.endsWith(\"\\n\")) n--;\n\treturn Math.max(n, 0);\n}\n\n/**\n * Multiset frequency delta: from → to.\n * deletions = lines over-represented in `from`; insertions = over-represented in `to`.\n */\nfunction bagOfLinesDelta(from: string, to: string): { insertions: number; deletions: number } {\n\tconst fromCounts = lineFrequency(from);\n\tconst toCounts = lineFrequency(to);\n\tlet insertions = 0;\n\tlet deletions = 0;\n\tconst keys = new Set([...fromCounts.keys(), ...toCounts.keys()]);\n\tfor (const key of keys) {\n\t\tconst a = fromCounts.get(key) ?? 0;\n\t\tconst b = toCounts.get(key) ?? 0;\n\t\tif (b > a) insertions += b - a;\n\t\telse if (a > b) deletions += a - b;\n\t}\n\treturn { insertions, deletions };\n}\n\nfunction lineFrequency(text: string): Map<string, number> {\n\tconst map = new Map<string, number>();\n\t// Preserve empty file as zero lines; otherwise split including last empty.\n\tif (text.length === 0) return map;\n\tconst lines = text.split(\"\\n\");\n\tif (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n\tfor (const line of lines) {\n\t\tmap.set(line, (map.get(line) ?? 0) + 1);\n\t}\n\treturn map;\n}\n\n/**\n * Restore the work tree to `target`. Returns the list of changed file paths.\n * Pass `onlyPaths` (from a prior collectChanges) to skip a second originChanged\n * pass and only touch those absolute paths.\n */\nexport async function applySnapshot(\n\tsid: string,\n\ttarget: FileHistorySnapshot,\n\topts?: ApplySnapshotOptions,\n): Promise<string[]> {\n\tconst state = getState(sid);\n\tconst cwd = cwdFor(sid);\n\tconst only = opts?.onlyPaths;\n\tconst trackings = Array.from(state.trackedFiles).filter((tracking) => {\n\t\tif (!only) return true;\n\t\treturn only.has(expand(tracking, cwd));\n\t});\n\n\tconst results = await mapPool(trackings, IO_CONCURRENCY, async (tracking) => {\n\t\ttry {\n\t\t\tconst filePath = expand(tracking, cwd);\n\t\t\tconst name = backupNameForTarget(state, target, tracking);\n\t\t\tif (name === undefined) return null; // can't resolve -> leave file alone\n\t\t\tif (name === null) {\n\t\t\t\ttry {\n\t\t\t\t\tawait unlink(filePath);\n\t\t\t\t\t// Worktree no longer matches any recorded identity.\n\t\t\t\t\tstate.lastSeen.delete(tracking);\n\t\t\t\t\treturn filePath;\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (!isENOENT(e)) throw e;\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// onlyPaths already proven different at confirm time — skip re-compare.\n\t\t\tif (only || (await originChanged(sid, filePath, name))) {\n\t\t\t\tawait restoreBackup(sid, filePath, name);\n\t\t\t\t// Drop stale lastSeen: worktree now matches the target blob, not the\n\t\t\t\t// latest backup identity beginTurn compares against.\n\t\t\t\tstate.lastSeen.delete(tracking);\n\t\t\t\treturn filePath;\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t});\n\treturn results.filter((p): p is string => p !== null);\n}\n\n// ---- persistence rebuild + resume migration -------------------------------\n\n/** The trailing window of `snapshots` retained under the cap (endTurn's ring). */\nexport function capSnapshots(snapshots: FileHistorySnapshot[], maxSnapshots = MAX_SNAPSHOTS): FileHistorySnapshot[] {\n\treturn snapshots.length > maxSnapshots ? snapshots.slice(-maxSnapshots) : snapshots;\n}\n\n/** Rebuild in-memory state from snapshots persisted in the session JSONL. */\nexport function restoreStateFromSnapshots(\n\tsid: string,\n\tcwd: string,\n\tsnapshots: FileHistorySnapshot[],\n\tmaxSnapshots = MAX_SNAPSHOTS,\n): void {\n\tcwds.set(sid, cwd);\n\t// Apply the same cap on reload so a long session's JSONL can't reinflate the\n\t// in-memory ring past the limit. trackedFiles is rebuilt from the retained\n\t// frames only (older frames are unreachable for rewind anyway); blobs only\n\t// those frames referenced are pruned best-effort, mirroring endTurn.\n\tconst retained = capSnapshots(snapshots, maxSnapshots);\n\tif (retained.length < snapshots.length) {\n\t\tvoid pruneDroppedBlobs(sid, snapshots.slice(0, snapshots.length - retained.length), retained);\n\t}\n\tconst trackedFiles = new Set<string>();\n\tfor (const snap of retained) {\n\t\tfor (const key of Object.keys(snap.trackedFileBackups)) trackedFiles.add(key);\n\t}\n\tstates.set(sid, {\n\t\tsnapshots: [...retained],\n\t\ttrackedFiles,\n\t\tlatestByTracking: rebuildLatestIndex(retained),\n\t\t// lastSeen is process-local; after reload we re-establish it on the first\n\t\t// beginTurn via originChanged / createBackup.\n\t\tlastSeen: new Map(),\n\t\tpending: null,\n\t\tdirty: false,\n\t\tseq: retained.length,\n\t});\n}\n\n/**\n * Hard-link this session's backup blobs from a previous session's directory\n * (resume/fork carries the conversation + its snapshot index, but the blobs live\n * under the old session id). Falls back to copy. No-op when ids match.\n */\nexport async function migrateBackupsFromSession(\n\tprevSid: string,\n\tsid: string,\n\tsnapshots: FileHistorySnapshot[],\n): Promise<void> {\n\tif (!prevSid || prevSid === sid) return;\n\tconst destDir = backupsDir(sid);\n\tawait mkdir(destDir, { recursive: true });\n\tawait Promise.allSettled(\n\t\tsnapshots.flatMap((snap) =>\n\t\t\tObject.values(snap.trackedFileBackups)\n\t\t\t\t.filter((b): b is FileBackup & { backupName: string } => b.backupName !== null)\n\t\t\t\t.map(async ({ backupName: name }) => {\n\t\t\t\t\tconst from = join(backupsDir(prevSid), name);\n\t\t\t\t\tconst to = join(destDir, name);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait link(from, to);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tconst code = (e as { code?: string }).code;\n\t\t\t\t\t\tif (code === \"EEXIST\") return;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait copyFile(from, to);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// best-effort; a missing blob just means that version can't restore\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t),\n\t);\n}\n"]}
@@ -1,35 +0,0 @@
1
- export interface SessionStorage {
2
- sessionId: string;
3
- dir: string;
4
- bytes: number;
5
- mtimeMs: number;
6
- orphan: boolean;
7
- }
8
- export interface GcResult {
9
- removed: number;
10
- reclaimedBytes: number;
11
- }
12
- /**
13
- * Extract a session id from a session JSONL path/filename (`..._<id>.jsonl`).
14
- * Format-agnostic (no hardcoded id shape) so it stays correct if Pi changes its
15
- * session-id format. Shared with the integration layer (index.ts) so the two
16
- * parsers can't drift — a stricter parser here could miss active sessions and
17
- * wrongly reap their backups as orphans.
18
- */
19
- export declare function sessionIdFromFile(file: string): string | undefined;
20
- /**
21
- * Session ids that still have a session JSONL under <agentDir>/sessions/.
22
- * Files are named `<ISO>_<sessionId>.jsonl`, nested one level under an
23
- * encoded-cwd directory.
24
- */
25
- export declare function activeSessionIds(): Set<string>;
26
- /** Inventory of on-disk backup storage (for the /rewind storage menu). */
27
- export declare function listSessions(currentSessionId?: string): SessionStorage[];
28
- /** Remove one session's backup directory. Returns bytes reclaimed, or null on failure. */
29
- export declare function removeSession(sessionId: string): number | null;
30
- /**
31
- * Reclaim aged + orphaned backup directories. Time-boxed and deletion-capped so
32
- * it never slows session_start. Skips age GC when retentionDays <= 0 ("forever").
33
- */
34
- export declare function runGc(retentionDays: number, currentSessionId?: string): GcResult;
35
- //# sourceMappingURL=gc.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/gc.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKlE;AA+BD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,CAAC,CAwB9C;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAoBxE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CA4BhF"}
@@ -1,169 +0,0 @@
1
- /**
2
- * gc.ts — storage reclamation for the rewind extension's backup blobs.
3
- *
4
- * Ported from Claude Code's cleanupOldFileHistoryBackups (src/utils/cleanup.ts):
5
- * each session's backup directory is reaped when its mtime ages past the
6
- * retention window. We add an orphan sweep — a backup dir whose session id has no
7
- * corresponding session JSONL (e.g. a crashed/aborted session) is reclaimed after
8
- * a short grace period.
9
- *
10
- * runGc() is called opportunistically at session_start; it is time-boxed and
11
- * caps deletions per run so it can never slow startup. listSessions()/removeSession()
12
- * back the /rewind storage menu.
13
- */
14
- import { readdirSync, rmSync, statSync } from "node:fs";
15
- import { basename, join } from "node:path";
16
- import { backupsDir, backupsRootDir, sessionsRootDir } from "./storage.js";
17
- const DAY_MS = 24 * 60 * 60 * 1000;
18
- const ORPHAN_GRACE_MS = 1 * DAY_MS;
19
- const GC_TIME_BUDGET_MS = 1500;
20
- const GC_MAX_DELETIONS = 50;
21
- /**
22
- * Extract a session id from a session JSONL path/filename (`..._<id>.jsonl`).
23
- * Format-agnostic (no hardcoded id shape) so it stays correct if Pi changes its
24
- * session-id format. Shared with the integration layer (index.ts) so the two
25
- * parsers can't drift — a stricter parser here could miss active sessions and
26
- * wrongly reap their backups as orphans.
27
- */
28
- export function sessionIdFromFile(file) {
29
- const b = basename(file).replace(/\.jsonl$/i, "");
30
- const us = b.lastIndexOf("_");
31
- const id = us >= 0 ? b.slice(us + 1) : b;
32
- return id || undefined;
33
- }
34
- /** Backup session directories on disk (one per session id). */
35
- function backupDirNames() {
36
- try {
37
- return readdirSync(backupsRootDir(), { withFileTypes: true })
38
- .filter((d) => d.isDirectory())
39
- .map((d) => d.name);
40
- }
41
- catch {
42
- return []; // root doesn't exist yet
43
- }
44
- }
45
- /** Total byte size of a directory's immediate files (backups are flat). */
46
- function dirSize(dir) {
47
- let bytes = 0;
48
- try {
49
- for (const d of readdirSync(dir, { withFileTypes: true })) {
50
- if (!d.isFile())
51
- continue;
52
- try {
53
- bytes += statSync(join(dir, d.name)).size;
54
- }
55
- catch {
56
- /* skip */
57
- }
58
- }
59
- }
60
- catch {
61
- /* skip */
62
- }
63
- return bytes;
64
- }
65
- /**
66
- * Session ids that still have a session JSONL under <agentDir>/sessions/.
67
- * Files are named `<ISO>_<sessionId>.jsonl`, nested one level under an
68
- * encoded-cwd directory.
69
- */
70
- export function activeSessionIds() {
71
- const ids = new Set();
72
- const root = sessionsRootDir();
73
- let projectDirs;
74
- try {
75
- projectDirs = readdirSync(root, { withFileTypes: true })
76
- .filter((d) => d.isDirectory())
77
- .map((d) => join(root, d.name));
78
- }
79
- catch {
80
- return ids;
81
- }
82
- for (const pd of projectDirs) {
83
- let files;
84
- try {
85
- files = readdirSync(pd);
86
- }
87
- catch {
88
- continue;
89
- }
90
- for (const f of files) {
91
- const id = sessionIdFromFile(f);
92
- if (id)
93
- ids.add(id);
94
- }
95
- }
96
- return ids;
97
- }
98
- /** Inventory of on-disk backup storage (for the /rewind storage menu). */
99
- export function listSessions(currentSessionId) {
100
- const active = activeSessionIds();
101
- const out = [];
102
- for (const sessionId of backupDirNames()) {
103
- const dir = backupsDir(sessionId);
104
- let mtimeMs = 0;
105
- try {
106
- mtimeMs = statSync(dir).mtimeMs;
107
- }
108
- catch {
109
- continue;
110
- }
111
- out.push({
112
- sessionId,
113
- dir,
114
- bytes: dirSize(dir),
115
- mtimeMs,
116
- orphan: sessionId !== currentSessionId && !active.has(sessionId),
117
- });
118
- }
119
- return out.sort((a, b) => b.mtimeMs - a.mtimeMs);
120
- }
121
- /** Remove one session's backup directory. Returns bytes reclaimed, or null on failure. */
122
- export function removeSession(sessionId) {
123
- const dir = backupsDir(sessionId);
124
- try {
125
- const bytes = dirSize(dir);
126
- rmSync(dir, { recursive: true, force: true });
127
- return bytes;
128
- }
129
- catch {
130
- return null;
131
- }
132
- }
133
- /**
134
- * Reclaim aged + orphaned backup directories. Time-boxed and deletion-capped so
135
- * it never slows session_start. Skips age GC when retentionDays <= 0 ("forever").
136
- */
137
- export function runGc(retentionDays, currentSessionId) {
138
- const start = Date.now();
139
- const ageCutoff = retentionDays > 0 ? start - retentionDays * DAY_MS : -Infinity;
140
- const orphanCutoff = start - ORPHAN_GRACE_MS;
141
- const active = activeSessionIds();
142
- let removed = 0;
143
- let reclaimedBytes = 0;
144
- for (const sessionId of backupDirNames()) {
145
- if (removed >= GC_MAX_DELETIONS || Date.now() - start > GC_TIME_BUDGET_MS)
146
- break;
147
- if (sessionId === currentSessionId)
148
- continue;
149
- const dir = backupsDir(sessionId);
150
- let mtimeMs;
151
- try {
152
- mtimeMs = statSync(dir).mtimeMs;
153
- }
154
- catch {
155
- continue;
156
- }
157
- const aged = mtimeMs < ageCutoff;
158
- const orphan = !active.has(sessionId) && mtimeMs < orphanCutoff;
159
- if (!aged && !orphan)
160
- continue;
161
- const bytes = removeSession(sessionId);
162
- if (bytes !== null) {
163
- removed++;
164
- reclaimedBytes += bytes;
165
- }
166
- }
167
- return { removed, reclaimedBytes };
168
- }
169
- //# sourceMappingURL=gc.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gc.js","sourceRoot":"","sources":["../../../src/extensions/rewind/gc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gc.ts — storage reclamation for the rewind extension's backup blobs.\n *\n * Ported from Claude Code's cleanupOldFileHistoryBackups (src/utils/cleanup.ts):\n * each session's backup directory is reaped when its mtime ages past the\n * retention window. We add an orphan sweep — a backup dir whose session id has no\n * corresponding session JSONL (e.g. a crashed/aborted session) is reclaimed after\n * a short grace period.\n *\n * runGc() is called opportunistically at session_start; it is time-boxed and\n * caps deletions per run so it can never slow startup. listSessions()/removeSession()\n * back the /rewind storage menu.\n */\nimport { readdirSync, rmSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\n\nimport { backupsDir, backupsRootDir, sessionsRootDir } from \"./storage.ts\";\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst ORPHAN_GRACE_MS = 1 * DAY_MS;\nconst GC_TIME_BUDGET_MS = 1500;\nconst GC_MAX_DELETIONS = 50;\n\nexport interface SessionStorage {\n\tsessionId: string;\n\tdir: string;\n\tbytes: number;\n\tmtimeMs: number;\n\torphan: boolean;\n}\n\nexport interface GcResult {\n\tremoved: number;\n\treclaimedBytes: number;\n}\n\n/**\n * Extract a session id from a session JSONL path/filename (`..._<id>.jsonl`).\n * Format-agnostic (no hardcoded id shape) so it stays correct if Pi changes its\n * session-id format. Shared with the integration layer (index.ts) so the two\n * parsers can't drift — a stricter parser here could miss active sessions and\n * wrongly reap their backups as orphans.\n */\nexport function sessionIdFromFile(file: string): string | undefined {\n\tconst b = basename(file).replace(/\\.jsonl$/i, \"\");\n\tconst us = b.lastIndexOf(\"_\");\n\tconst id = us >= 0 ? b.slice(us + 1) : b;\n\treturn id || undefined;\n}\n\n/** Backup session directories on disk (one per session id). */\nfunction backupDirNames(): string[] {\n\ttry {\n\t\treturn readdirSync(backupsRootDir(), { withFileTypes: true })\n\t\t\t.filter((d) => d.isDirectory())\n\t\t\t.map((d) => d.name);\n\t} catch {\n\t\treturn []; // root doesn't exist yet\n\t}\n}\n\n/** Total byte size of a directory's immediate files (backups are flat). */\nfunction dirSize(dir: string): number {\n\tlet bytes = 0;\n\ttry {\n\t\tfor (const d of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (!d.isFile()) continue;\n\t\t\ttry {\n\t\t\t\tbytes += statSync(join(dir, d.name)).size;\n\t\t\t} catch {\n\t\t\t\t/* skip */\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* skip */\n\t}\n\treturn bytes;\n}\n\n/**\n * Session ids that still have a session JSONL under <agentDir>/sessions/.\n * Files are named `<ISO>_<sessionId>.jsonl`, nested one level under an\n * encoded-cwd directory.\n */\nexport function activeSessionIds(): Set<string> {\n\tconst ids = new Set<string>();\n\tconst root = sessionsRootDir();\n\tlet projectDirs: string[];\n\ttry {\n\t\tprojectDirs = readdirSync(root, { withFileTypes: true })\n\t\t\t.filter((d) => d.isDirectory())\n\t\t\t.map((d) => join(root, d.name));\n\t} catch {\n\t\treturn ids;\n\t}\n\tfor (const pd of projectDirs) {\n\t\tlet files: string[];\n\t\ttry {\n\t\t\tfiles = readdirSync(pd);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const f of files) {\n\t\t\tconst id = sessionIdFromFile(f);\n\t\t\tif (id) ids.add(id);\n\t\t}\n\t}\n\treturn ids;\n}\n\n/** Inventory of on-disk backup storage (for the /rewind storage menu). */\nexport function listSessions(currentSessionId?: string): SessionStorage[] {\n\tconst active = activeSessionIds();\n\tconst out: SessionStorage[] = [];\n\tfor (const sessionId of backupDirNames()) {\n\t\tconst dir = backupsDir(sessionId);\n\t\tlet mtimeMs = 0;\n\t\ttry {\n\t\t\tmtimeMs = statSync(dir).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tout.push({\n\t\t\tsessionId,\n\t\t\tdir,\n\t\t\tbytes: dirSize(dir),\n\t\t\tmtimeMs,\n\t\t\torphan: sessionId !== currentSessionId && !active.has(sessionId),\n\t\t});\n\t}\n\treturn out.sort((a, b) => b.mtimeMs - a.mtimeMs);\n}\n\n/** Remove one session's backup directory. Returns bytes reclaimed, or null on failure. */\nexport function removeSession(sessionId: string): number | null {\n\tconst dir = backupsDir(sessionId);\n\ttry {\n\t\tconst bytes = dirSize(dir);\n\t\trmSync(dir, { recursive: true, force: true });\n\t\treturn bytes;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Reclaim aged + orphaned backup directories. Time-boxed and deletion-capped so\n * it never slows session_start. Skips age GC when retentionDays <= 0 (\"forever\").\n */\nexport function runGc(retentionDays: number, currentSessionId?: string): GcResult {\n\tconst start = Date.now();\n\tconst ageCutoff = retentionDays > 0 ? start - retentionDays * DAY_MS : -Infinity;\n\tconst orphanCutoff = start - ORPHAN_GRACE_MS;\n\tconst active = activeSessionIds();\n\tlet removed = 0;\n\tlet reclaimedBytes = 0;\n\n\tfor (const sessionId of backupDirNames()) {\n\t\tif (removed >= GC_MAX_DELETIONS || Date.now() - start > GC_TIME_BUDGET_MS) break;\n\t\tif (sessionId === currentSessionId) continue;\n\t\tconst dir = backupsDir(sessionId);\n\t\tlet mtimeMs: number;\n\t\ttry {\n\t\t\tmtimeMs = statSync(dir).mtimeMs;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst aged = mtimeMs < ageCutoff;\n\t\tconst orphan = !active.has(sessionId) && mtimeMs < orphanCutoff;\n\t\tif (!aged && !orphan) continue;\n\t\tconst bytes = removeSession(sessionId);\n\t\tif (bytes !== null) {\n\t\t\tremoved++;\n\t\t\treclaimedBytes += bytes;\n\t\t}\n\t}\n\treturn { removed, reclaimedBytes };\n}\n"]}
@@ -1,3 +0,0 @@
1
- import type { ExtensionAPI } from "../../core/extensions/types.ts";
2
- export default function rewind(pi: ExtensionAPI): void;
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/index.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gCAAgC,CAAC;AA8GrF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA6KrD"}