@quantiya/codevibe-claude-plugin 2.0.11 → 2.0.12

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 (276) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/server.js +18 -18
  3. package/hooks/session-start.sh +6 -4
  4. package/node_modules/@quantiya/codevibe-core/dist/index.d.ts +1 -1
  5. package/node_modules/@quantiya/codevibe-core/dist/index.js +259 -259
  6. package/node_modules/@quantiya/codevibe-core/dist/session/index.d.ts +1 -1
  7. package/node_modules/@quantiya/codevibe-core/dist/session/session-resume.d.ts +12 -0
  8. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  9. package/node_modules/es-toolkit/CHANGELOG.md +38 -3
  10. package/node_modules/es-toolkit/dist/browser.global.js +4 -4
  11. package/node_modules/es-toolkit/dist/compat/array/differenceWith.js +2 -1
  12. package/node_modules/es-toolkit/dist/compat/array/differenceWith.mjs +2 -1
  13. package/node_modules/es-toolkit/dist/compat/array/findLastIndex.js +5 -3
  14. package/node_modules/es-toolkit/dist/compat/array/findLastIndex.mjs +5 -3
  15. package/node_modules/es-toolkit/dist/compat/array/sortedIndexBy.js +1 -1
  16. package/node_modules/es-toolkit/dist/compat/array/sortedIndexBy.mjs +1 -1
  17. package/node_modules/es-toolkit/dist/compat/array/without.js +8 -2
  18. package/node_modules/es-toolkit/dist/compat/array/without.mjs +8 -2
  19. package/node_modules/es-toolkit/dist/compat/array/xorWith.js +4 -6
  20. package/node_modules/es-toolkit/dist/compat/array/xorWith.mjs +4 -6
  21. package/node_modules/es-toolkit/dist/compat/compat.js +1 -1
  22. package/node_modules/es-toolkit/dist/compat/compat.mjs +1 -1
  23. package/node_modules/es-toolkit/dist/compat/function/debounce.js +1 -1
  24. package/node_modules/es-toolkit/dist/compat/function/debounce.mjs +1 -1
  25. package/node_modules/es-toolkit/dist/compat/index.js +1 -1
  26. package/node_modules/es-toolkit/dist/compat/index.mjs +1 -1
  27. package/node_modules/es-toolkit/dist/compat/object/has.js +3 -2
  28. package/node_modules/es-toolkit/dist/compat/object/has.mjs +3 -2
  29. package/node_modules/es-toolkit/dist/compat/object/hasIn.js +3 -2
  30. package/node_modules/es-toolkit/dist/compat/object/hasIn.mjs +3 -2
  31. package/node_modules/es-toolkit/dist/compat/object/mergeWith.js +1 -1
  32. package/node_modules/es-toolkit/dist/compat/object/mergeWith.mjs +1 -1
  33. package/node_modules/es-toolkit/dist/compat/object/omitBy.js +1 -1
  34. package/node_modules/es-toolkit/dist/compat/object/omitBy.mjs +1 -1
  35. package/node_modules/es-toolkit/dist/compat/object/pickBy.js +1 -1
  36. package/node_modules/es-toolkit/dist/compat/object/pickBy.mjs +1 -1
  37. package/node_modules/es-toolkit/dist/compat/util/toString.js +3 -1
  38. package/node_modules/es-toolkit/dist/compat/util/toString.mjs +3 -1
  39. package/node_modules/es-toolkit/dist/fp/flowAsync.d.mts +213 -0
  40. package/node_modules/es-toolkit/dist/fp/flowAsync.d.ts +213 -0
  41. package/node_modules/es-toolkit/dist/fp/flowAsync.js +37 -0
  42. package/node_modules/es-toolkit/dist/fp/flowAsync.mjs +37 -0
  43. package/node_modules/es-toolkit/dist/fp/index.d.mts +2 -1
  44. package/node_modules/es-toolkit/dist/fp/index.d.ts +2 -1
  45. package/node_modules/es-toolkit/dist/fp/index.js +2 -0
  46. package/node_modules/es-toolkit/dist/fp/index.mjs +2 -1
  47. package/node_modules/es-toolkit/dist/fp/iterator/cartesianProduct.d.mts +23 -0
  48. package/node_modules/es-toolkit/dist/fp/iterator/cartesianProduct.d.ts +23 -0
  49. package/node_modules/es-toolkit/dist/fp/iterator/cartesianProduct.js +28 -0
  50. package/node_modules/es-toolkit/dist/fp/iterator/cartesianProduct.mjs +28 -0
  51. package/node_modules/es-toolkit/dist/fp/iterator/chunk.d.mts +19 -0
  52. package/node_modules/es-toolkit/dist/fp/iterator/chunk.d.ts +19 -0
  53. package/node_modules/es-toolkit/dist/fp/iterator/chunk.js +24 -0
  54. package/node_modules/es-toolkit/dist/fp/iterator/chunk.mjs +24 -0
  55. package/node_modules/es-toolkit/dist/fp/iterator/count.d.mts +20 -0
  56. package/node_modules/es-toolkit/dist/fp/iterator/count.d.ts +20 -0
  57. package/node_modules/es-toolkit/dist/fp/iterator/count.js +25 -0
  58. package/node_modules/es-toolkit/dist/fp/iterator/count.mjs +25 -0
  59. package/node_modules/es-toolkit/dist/fp/iterator/drop.d.mts +20 -0
  60. package/node_modules/es-toolkit/dist/fp/iterator/drop.d.ts +20 -0
  61. package/node_modules/es-toolkit/dist/fp/iterator/drop.js +24 -0
  62. package/node_modules/es-toolkit/dist/fp/iterator/drop.mjs +24 -0
  63. package/node_modules/es-toolkit/dist/fp/iterator/dropWhile.d.mts +18 -0
  64. package/node_modules/es-toolkit/dist/fp/iterator/dropWhile.d.ts +18 -0
  65. package/node_modules/es-toolkit/dist/fp/iterator/dropWhile.js +23 -0
  66. package/node_modules/es-toolkit/dist/fp/iterator/dropWhile.mjs +23 -0
  67. package/node_modules/es-toolkit/dist/fp/iterator/every.d.mts +20 -0
  68. package/node_modules/es-toolkit/dist/fp/iterator/every.d.ts +20 -0
  69. package/node_modules/es-toolkit/dist/fp/iterator/every.js +24 -0
  70. package/node_modules/es-toolkit/dist/fp/iterator/every.mjs +24 -0
  71. package/node_modules/es-toolkit/dist/fp/iterator/filter.d.mts +37 -0
  72. package/node_modules/es-toolkit/dist/fp/iterator/filter.d.ts +37 -0
  73. package/node_modules/es-toolkit/dist/fp/iterator/filter.js +8 -0
  74. package/node_modules/es-toolkit/dist/fp/iterator/filter.mjs +8 -0
  75. package/node_modules/es-toolkit/dist/fp/iterator/find.d.mts +20 -0
  76. package/node_modules/es-toolkit/dist/fp/iterator/find.d.ts +20 -0
  77. package/node_modules/es-toolkit/dist/fp/iterator/find.js +24 -0
  78. package/node_modules/es-toolkit/dist/fp/iterator/find.mjs +24 -0
  79. package/node_modules/es-toolkit/dist/fp/iterator/flatMap.d.mts +20 -0
  80. package/node_modules/es-toolkit/dist/fp/iterator/flatMap.d.ts +20 -0
  81. package/node_modules/es-toolkit/dist/fp/iterator/flatMap.js +24 -0
  82. package/node_modules/es-toolkit/dist/fp/iterator/flatMap.mjs +24 -0
  83. package/node_modules/es-toolkit/dist/fp/iterator/forEach.d.mts +22 -0
  84. package/node_modules/es-toolkit/dist/fp/iterator/forEach.d.ts +22 -0
  85. package/node_modules/es-toolkit/dist/fp/iterator/forEach.js +26 -0
  86. package/node_modules/es-toolkit/dist/fp/iterator/forEach.mjs +26 -0
  87. package/node_modules/es-toolkit/dist/fp/iterator/head.d.mts +19 -0
  88. package/node_modules/es-toolkit/dist/fp/iterator/head.d.ts +19 -0
  89. package/node_modules/es-toolkit/dist/fp/iterator/head.js +24 -0
  90. package/node_modules/es-toolkit/dist/fp/iterator/head.mjs +24 -0
  91. package/node_modules/es-toolkit/dist/fp/iterator/index.d.mts +22 -0
  92. package/node_modules/es-toolkit/dist/fp/iterator/index.d.ts +22 -0
  93. package/node_modules/es-toolkit/dist/fp/iterator/index.js +43 -0
  94. package/node_modules/es-toolkit/dist/fp/iterator/index.mjs +22 -0
  95. package/node_modules/es-toolkit/dist/fp/iterator/map.d.mts +20 -0
  96. package/node_modules/es-toolkit/dist/fp/iterator/map.d.ts +20 -0
  97. package/node_modules/es-toolkit/dist/fp/iterator/map.js +24 -0
  98. package/node_modules/es-toolkit/dist/fp/iterator/map.mjs +24 -0
  99. package/node_modules/es-toolkit/dist/fp/iterator/partition.d.mts +22 -0
  100. package/node_modules/es-toolkit/dist/fp/iterator/partition.d.ts +22 -0
  101. package/node_modules/es-toolkit/dist/fp/iterator/partition.js +27 -0
  102. package/node_modules/es-toolkit/dist/fp/iterator/partition.mjs +27 -0
  103. package/node_modules/es-toolkit/dist/fp/iterator/reduce.d.mts +24 -0
  104. package/node_modules/es-toolkit/dist/fp/iterator/reduce.d.ts +24 -0
  105. package/node_modules/es-toolkit/dist/fp/iterator/reduce.js +28 -0
  106. package/node_modules/es-toolkit/dist/fp/iterator/reduce.mjs +28 -0
  107. package/node_modules/es-toolkit/dist/fp/iterator/scan.d.mts +21 -0
  108. package/node_modules/es-toolkit/dist/fp/iterator/scan.d.ts +21 -0
  109. package/node_modules/es-toolkit/dist/fp/iterator/scan.js +26 -0
  110. package/node_modules/es-toolkit/dist/fp/iterator/scan.mjs +26 -0
  111. package/node_modules/es-toolkit/dist/fp/iterator/some.d.mts +20 -0
  112. package/node_modules/es-toolkit/dist/fp/iterator/some.d.ts +20 -0
  113. package/node_modules/es-toolkit/dist/fp/iterator/some.js +24 -0
  114. package/node_modules/es-toolkit/dist/fp/iterator/some.mjs +24 -0
  115. package/node_modules/es-toolkit/dist/fp/iterator/take.d.mts +21 -0
  116. package/node_modules/es-toolkit/dist/fp/iterator/take.d.ts +21 -0
  117. package/node_modules/es-toolkit/dist/fp/iterator/take.js +25 -0
  118. package/node_modules/es-toolkit/dist/fp/iterator/take.mjs +25 -0
  119. package/node_modules/es-toolkit/dist/fp/iterator/takeWhile.d.mts +19 -0
  120. package/node_modules/es-toolkit/dist/fp/iterator/takeWhile.d.ts +19 -0
  121. package/node_modules/es-toolkit/dist/fp/iterator/takeWhile.js +24 -0
  122. package/node_modules/es-toolkit/dist/fp/iterator/takeWhile.mjs +24 -0
  123. package/node_modules/es-toolkit/dist/fp/iterator/toArray.d.mts +21 -0
  124. package/node_modules/es-toolkit/dist/fp/iterator/toArray.d.ts +21 -0
  125. package/node_modules/es-toolkit/dist/fp/iterator/toArray.js +25 -0
  126. package/node_modules/es-toolkit/dist/fp/iterator/toArray.mjs +25 -0
  127. package/node_modules/es-toolkit/dist/fp/iterator/uniqBy.d.mts +20 -0
  128. package/node_modules/es-toolkit/dist/fp/iterator/uniqBy.d.ts +20 -0
  129. package/node_modules/es-toolkit/dist/fp/iterator/uniqBy.js +25 -0
  130. package/node_modules/es-toolkit/dist/fp/iterator/uniqBy.mjs +25 -0
  131. package/node_modules/es-toolkit/dist/fp/iterator/zip.d.mts +20 -0
  132. package/node_modules/es-toolkit/dist/fp/iterator/zip.d.ts +20 -0
  133. package/node_modules/es-toolkit/dist/fp/iterator/zip.js +25 -0
  134. package/node_modules/es-toolkit/dist/fp/iterator/zip.mjs +25 -0
  135. package/node_modules/es-toolkit/dist/index.d.mts +3 -1
  136. package/node_modules/es-toolkit/dist/index.d.ts +3 -1
  137. package/node_modules/es-toolkit/dist/index.js +4 -0
  138. package/node_modules/es-toolkit/dist/index.mjs +3 -1
  139. package/node_modules/es-toolkit/dist/iterator/_internal/iterator.js +74 -0
  140. package/node_modules/es-toolkit/dist/iterator/_internal/iterator.mjs +74 -0
  141. package/node_modules/es-toolkit/dist/iterator/cartesianProduct.d.mts +33 -0
  142. package/node_modules/es-toolkit/dist/iterator/cartesianProduct.d.ts +33 -0
  143. package/node_modules/es-toolkit/dist/iterator/cartesianProduct.js +95 -0
  144. package/node_modules/es-toolkit/dist/iterator/cartesianProduct.mjs +95 -0
  145. package/node_modules/es-toolkit/dist/iterator/chunk.d.mts +21 -0
  146. package/node_modules/es-toolkit/dist/iterator/chunk.d.ts +21 -0
  147. package/node_modules/es-toolkit/dist/iterator/chunk.js +40 -0
  148. package/node_modules/es-toolkit/dist/iterator/chunk.mjs +40 -0
  149. package/node_modules/es-toolkit/dist/iterator/count.d.mts +18 -0
  150. package/node_modules/es-toolkit/dist/iterator/count.d.ts +18 -0
  151. package/node_modules/es-toolkit/dist/iterator/count.js +26 -0
  152. package/node_modules/es-toolkit/dist/iterator/count.mjs +26 -0
  153. package/node_modules/es-toolkit/dist/iterator/dropWhile.d.mts +20 -0
  154. package/node_modules/es-toolkit/dist/iterator/dropWhile.d.ts +20 -0
  155. package/node_modules/es-toolkit/dist/iterator/dropWhile.js +52 -0
  156. package/node_modules/es-toolkit/dist/iterator/dropWhile.mjs +52 -0
  157. package/node_modules/es-toolkit/dist/iterator/head.d.mts +21 -0
  158. package/node_modules/es-toolkit/dist/iterator/head.d.ts +21 -0
  159. package/node_modules/es-toolkit/dist/iterator/head.js +26 -0
  160. package/node_modules/es-toolkit/dist/iterator/head.mjs +26 -0
  161. package/node_modules/es-toolkit/dist/iterator/index.d.mts +13 -0
  162. package/node_modules/es-toolkit/dist/iterator/index.d.ts +13 -0
  163. package/node_modules/es-toolkit/dist/iterator/index.js +25 -0
  164. package/node_modules/es-toolkit/dist/iterator/index.mjs +13 -0
  165. package/node_modules/es-toolkit/dist/iterator/iterate.d.mts +20 -0
  166. package/node_modules/es-toolkit/dist/iterator/iterate.d.ts +20 -0
  167. package/node_modules/es-toolkit/dist/iterator/iterate.js +32 -0
  168. package/node_modules/es-toolkit/dist/iterator/iterate.mjs +32 -0
  169. package/node_modules/es-toolkit/dist/iterator/partition.d.mts +18 -0
  170. package/node_modules/es-toolkit/dist/iterator/partition.d.ts +18 -0
  171. package/node_modules/es-toolkit/dist/iterator/partition.js +36 -0
  172. package/node_modules/es-toolkit/dist/iterator/partition.mjs +36 -0
  173. package/node_modules/es-toolkit/dist/iterator/range.d.mts +42 -0
  174. package/node_modules/es-toolkit/dist/iterator/range.d.ts +42 -0
  175. package/node_modules/es-toolkit/dist/iterator/range.js +25 -0
  176. package/node_modules/es-toolkit/dist/iterator/range.mjs +25 -0
  177. package/node_modules/es-toolkit/dist/iterator/scan.d.mts +23 -0
  178. package/node_modules/es-toolkit/dist/iterator/scan.d.ts +23 -0
  179. package/node_modules/es-toolkit/dist/iterator/scan.js +47 -0
  180. package/node_modules/es-toolkit/dist/iterator/scan.mjs +47 -0
  181. package/node_modules/es-toolkit/dist/iterator/takeWhile.d.mts +20 -0
  182. package/node_modules/es-toolkit/dist/iterator/takeWhile.d.ts +20 -0
  183. package/node_modules/es-toolkit/dist/iterator/takeWhile.js +34 -0
  184. package/node_modules/es-toolkit/dist/iterator/takeWhile.mjs +34 -0
  185. package/node_modules/es-toolkit/dist/iterator/uniqBy.d.mts +22 -0
  186. package/node_modules/es-toolkit/dist/iterator/uniqBy.d.ts +22 -0
  187. package/node_modules/es-toolkit/dist/iterator/uniqBy.js +43 -0
  188. package/node_modules/es-toolkit/dist/iterator/uniqBy.mjs +43 -0
  189. package/node_modules/es-toolkit/dist/iterator/zip.d.mts +23 -0
  190. package/node_modules/es-toolkit/dist/iterator/zip.d.ts +23 -0
  191. package/node_modules/es-toolkit/dist/iterator/zip.js +45 -0
  192. package/node_modules/es-toolkit/dist/iterator/zip.mjs +45 -0
  193. package/node_modules/es-toolkit/dist/object/merge.d.mts +3 -1
  194. package/node_modules/es-toolkit/dist/object/merge.d.ts +3 -1
  195. package/node_modules/es-toolkit/dist/object/merge.js +0 -40
  196. package/node_modules/es-toolkit/dist/object/merge.mjs +0 -40
  197. package/node_modules/es-toolkit/dist/object/omitBy.d.mts +4 -2
  198. package/node_modules/es-toolkit/dist/object/omitBy.d.ts +4 -2
  199. package/node_modules/es-toolkit/dist/object/omitBy.js +4 -3
  200. package/node_modules/es-toolkit/dist/object/omitBy.mjs +4 -3
  201. package/node_modules/es-toolkit/dist/object/pickBy.d.mts +4 -2
  202. package/node_modules/es-toolkit/dist/object/pickBy.d.ts +4 -2
  203. package/node_modules/es-toolkit/dist/object/pickBy.js +4 -3
  204. package/node_modules/es-toolkit/dist/object/pickBy.mjs +4 -3
  205. package/node_modules/es-toolkit/dist/types/EmptyObject.d.mts +21 -0
  206. package/node_modules/es-toolkit/dist/types/EmptyObject.d.ts +21 -0
  207. package/node_modules/es-toolkit/dist/types/IsEqual.d.mts +18 -0
  208. package/node_modules/es-toolkit/dist/types/IsEqual.d.ts +18 -0
  209. package/node_modules/es-toolkit/dist/types/JSONValue.d.mts +24 -0
  210. package/node_modules/es-toolkit/dist/types/JSONValue.d.ts +24 -0
  211. package/node_modules/es-toolkit/dist/types/Merge.d.mts +37 -0
  212. package/node_modules/es-toolkit/dist/types/Merge.d.ts +37 -0
  213. package/node_modules/es-toolkit/dist/types/ObjectKeys.d.mts +19 -0
  214. package/node_modules/es-toolkit/dist/types/ObjectKeys.d.ts +19 -0
  215. package/node_modules/es-toolkit/dist/types/Primitive.d.mts +14 -0
  216. package/node_modules/es-toolkit/dist/types/Primitive.d.ts +14 -0
  217. package/node_modules/es-toolkit/dist/types/SetOptional.d.mts +20 -0
  218. package/node_modules/es-toolkit/dist/types/SetOptional.d.ts +20 -0
  219. package/node_modules/es-toolkit/dist/types/SetRequired.d.mts +20 -0
  220. package/node_modules/es-toolkit/dist/types/SetRequired.d.ts +20 -0
  221. package/node_modules/es-toolkit/dist/types/UnknownRecord.d.mts +24 -0
  222. package/node_modules/es-toolkit/dist/types/UnknownRecord.d.ts +24 -0
  223. package/node_modules/es-toolkit/dist/types/index.d.mts +11 -2
  224. package/node_modules/es-toolkit/dist/types/index.d.ts +11 -2
  225. package/node_modules/es-toolkit/dist/util/attempt.d.mts +1 -1
  226. package/node_modules/es-toolkit/dist/util/attempt.d.ts +1 -1
  227. package/node_modules/es-toolkit/dist/util/attemptAsync.d.mts +1 -1
  228. package/node_modules/es-toolkit/dist/util/attemptAsync.d.ts +1 -1
  229. package/node_modules/es-toolkit/dist/util/defer.d.mts +27 -0
  230. package/node_modules/es-toolkit/dist/util/defer.d.ts +27 -0
  231. package/node_modules/es-toolkit/dist/util/defer.js +31 -0
  232. package/node_modules/es-toolkit/dist/util/defer.mjs +31 -0
  233. package/node_modules/es-toolkit/dist/util/deferAsync.d.mts +30 -0
  234. package/node_modules/es-toolkit/dist/util/deferAsync.d.ts +30 -0
  235. package/node_modules/es-toolkit/dist/util/deferAsync.js +34 -0
  236. package/node_modules/es-toolkit/dist/util/deferAsync.mjs +34 -0
  237. package/node_modules/es-toolkit/dist/util/index.d.mts +3 -1
  238. package/node_modules/es-toolkit/dist/util/index.d.ts +3 -1
  239. package/node_modules/es-toolkit/dist/util/index.js +4 -0
  240. package/node_modules/es-toolkit/dist/util/index.mjs +3 -1
  241. package/node_modules/es-toolkit/fp/iterator.d.ts +1 -0
  242. package/node_modules/es-toolkit/fp/iterator.js +1 -0
  243. package/node_modules/es-toolkit/iterator.d.ts +1 -0
  244. package/node_modules/es-toolkit/iterator.js +1 -0
  245. package/node_modules/es-toolkit/package.json +44 -1
  246. package/node_modules/node-abi/abi_registry.json +7 -0
  247. package/node_modules/node-abi/package.json +1 -1
  248. package/package.json +2 -2
  249. package/node_modules/fs-ext/build/Makefile +0 -347
  250. package/node_modules/fs-ext/build/Release/.deps/Release/fs_ext.node.d +0 -1
  251. package/node_modules/fs-ext/build/Release/.deps/Release/obj.target/fs_ext/fs-ext.o.d +0 -165
  252. package/node_modules/fs-ext/build/Release/fs_ext.node +0 -0
  253. package/node_modules/fs-ext/build/Release/obj.target/fs_ext/fs-ext.o +0 -0
  254. package/node_modules/fs-ext/build/binding.Makefile +0 -6
  255. package/node_modules/fs-ext/build/config.gypi +0 -503
  256. package/node_modules/fs-ext/build/fs_ext.target.mk +0 -183
  257. package/node_modules/fs-ext/build/gyp-mac-tool +0 -768
  258. package/node_modules/keytar/build/Release/keytar.node +0 -0
  259. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/LICENSE.md +0 -0
  260. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/README.md +0 -0
  261. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/common.gypi +0 -0
  262. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/except.gypi +0 -0
  263. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/index.js +0 -0
  264. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/napi-inl.deprecated.h +0 -0
  265. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/napi-inl.h +0 -0
  266. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/napi.h +0 -0
  267. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/node_api.gyp +0 -0
  268. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/noexcept.gypi +0 -0
  269. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/nothing.c +0 -0
  270. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/package-support.json +0 -0
  271. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/package.json +0 -0
  272. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/tools/README.md +0 -0
  273. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/tools/check-napi.js +0 -0
  274. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/tools/clang-format.js +0 -0
  275. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/tools/conversion.js +0 -0
  276. /package/node_modules/{node-addon-api → keytar/node_modules/node-addon-api}/tools/eslint-format.js +0 -0
package/dist/server.js CHANGED
@@ -1,13 +1,13 @@
1
- "use strict";var Ke=Object.create;var te=Object.defineProperty;var De=Object.getOwnPropertyDescriptor;var Qe=Object.getOwnPropertyNames;var Le=Object.getPrototypeOf,$e=Object.prototype.hasOwnProperty;var Ve=(y,e)=>{for(var t in e)te(y,t,{get:e[t],enumerable:!0})},fe=(y,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Qe(e))!$e.call(y,n)&&n!==t&&te(y,n,{get:()=>e[n],enumerable:!(i=De(e,n))||i.enumerable});return y};var Q=(y,e,t)=>(t=y!=null?Ke(Le(y)):{},fe(e||!y||!y.__esModule?te(t,"default",{value:y,enumerable:!0}):t,y)),Ge=y=>fe(te({},"__esModule",{value:!0}),y);var rt={};Ve(rt,{McpServer:()=>ce,classifyApplyUserDecisionEvent:()=>Fe,classifyOrchestrationResponse:()=>it,formatOrchestrationBannerText:()=>Ne,parseInteractivePromptInput:()=>Me,shouldFallThroughForBridgeUnavailable:()=>Ue,shutdownExitCode:()=>_e});module.exports=Ge(rt);var D=Q(require("fs")),G=Q(require("path")),W=Q(require("os")),Re=require("child_process"),Ae=require("util"),xe=require("child_process"),j=require("crypto");var ye=Q(require("os")),ve=Q(require("path")),we=require("@quantiya/codevibe-core"),r=(0,we.createLogger)({name:"codevibe-claude",logFile:ve.default.join(ye.default.tmpdir(),"codevibe-claude-mcp.log"),level:"info"});var d=require("@quantiya/codevibe-core"),Oe=require("@quantiya/quorum-core"),ae=Q(require("ws"));var de=Q(require("express")),X=Q(require("fs")),pe=Q(require("path")),le=Q(require("os")),Se=require("@quantiya/codevibe-core");var T=require("@quantiya/codevibe-core");var ne=class{constructor(e=()=>({status:"healthy"})){this.assignedPort=0;this.healthProvider=e,this.app=(0,de.default)(),this.setupMiddleware(),this.setupRoutes()}setSessionId(e){this.sessionId=e}getPort(){return this.assignedPort}setupMiddleware(){this.app.use(de.default.json({limit:"1mb"})),this.app.use((e,t,i)=>{r.debug(`${e.method} ${e.path}`,{body:e.body,query:e.query}),i()}),this.app.use((e,t,i,n)=>{r.error("Express error:",e);let s={success:!1,error:e.message||"Internal server error"};i.status(500).json(s)})}setupRoutes(){this.app.get("/health",this.handleHealth.bind(this)),this.app.post("/event",this.handleEvent.bind(this)),process.env.NODE_ENV!=="production"&&this.app.post("/test/execute",this.handleTestExecute.bind(this))}handleHealth(e,t){let i=this.healthProvider();t.json({success:!0,service:"codevibe-claude",data:{...i,uptime:process.uptime(),version:"0.1.0",timestamp:new Date().toISOString()}})}async handleEvent(e,t){try{let i=e.body;if(!i.session_id){let a={success:!1,error:"Missing required field: session_id"};t.status(400).json(a);return}if(!i.hook_event_name){let a={success:!1,error:"Missing required field: hook_event_name"};t.status(400).json(a);return}let n=this.transformHookToEvent(i);r.info("Received event from hook",{sessionId:i.session_id,hookEvent:i.hook_event_name,type:n.type}),this.eventHandler?await this.eventHandler(n):r.warn("No event handler registered");let s={success:!0,message:"Event processed successfully"};t.json(s)}catch(i){r.error("Error handling event:",i);let n={success:!1,error:i instanceof Error?i.message:"Unknown error"};t.status(500).json(n)}}async handleTestExecute(e,t){try{let{sessionId:i,prompt:n}=e.body;if(!i||!n){let a={success:!1,error:"Missing required fields: sessionId, prompt"};t.status(400).json(a);return}r.info("Test execute request",{sessionId:i,prompt:n});let s={success:!0,message:"Test execution endpoint - not implemented yet",data:{sessionId:i,prompt:n}};t.json(s)}catch(i){r.error("Error in test execute:",i);let n={success:!1,error:i instanceof Error?i.message:"Unknown error"};t.status(500).json(n)}}transformHookToEvent(e){let t,i,n={cwd:e.cwd,hook_event_name:e.hook_event_name,...e.metadata||{}};if(e.type&&e.content!==void 0)t=e.type,i=e.content;else switch(e.hook_event_name){case"SessionStart":t=T.EventType.NOTIFICATION,i="Session started",n.source=e.source;break;case"SessionEnd":t=T.EventType.NOTIFICATION,i=`Session ended: ${e.reason||"unknown"}`,n.reason=e.reason;break;case"UserPromptSubmit":t=T.EventType.USER_PROMPT,i=e.prompt||"";break;case"PostToolUse":t=T.EventType.TOOL_USE,i=JSON.stringify({tool_name:e.tool_name,tool_input:e.tool_input,tool_response:e.tool_response}),n.tool_name=e.tool_name;break;case"Notification":t=T.EventType.NOTIFICATION,i=e.message||"",n.notification_type=e.notification_type;break;default:t=T.EventType.NOTIFICATION,i=`Hook event: ${e.hook_event_name}`}return{session_id:e.session_id,hook_event_name:e.hook_event_name,type:t,source:T.EventSource.DESKTOP,content:i,metadata:n,tool_use_id:e.tool_use_id,tool_input:e.tool_input,transcript_path:e.transcript_path}}onEvent(e){this.eventHandler=e}async start(e){let t=e||this.sessionId;return t&&(this.sessionId=t),new Promise((i,n)=>{try{let s=(0,Se.getConfig)(),a=s.server.dynamicPort?0:s.server.port;this.server=this.app.listen(a,s.server.host,()=>{let o=this.server.address();this.assignedPort=o.port,r.info(`HTTP API listening on http://${s.server.host}:${this.assignedPort}`),this.sessionId&&this.writePortFile(this.sessionId,this.assignedPort),i(this.assignedPort)}),this.server.on("error",o=>{r.error("HTTP server error:",o),n(o)})}catch(s){n(s)}})}writePortFile(e,t){let i=pe.join(le.tmpdir(),`codevibe-claude-${e}.port`);try{X.writeFileSync(i,t.toString()),r.info(`Port file written: ${i} -> ${t}`)}catch(n){r.error(`Failed to write port file: ${i}`,n)}}removePortFile(){if(this.sessionId){let e=pe.join(le.tmpdir(),`codevibe-claude-${this.sessionId}.port`);try{X.existsSync(e)&&(X.unlinkSync(e),r.info(`Port file removed: ${e}`))}catch(t){r.warn(`Failed to remove port file: ${e}`,t)}}}async stop(e){return new Promise((t,i)=>{this.sessionId&&e?.protectedSessionIds?.has(this.sessionId)?r.info("Skipping port file removal \u2014 another daemon still serves this session",{sessionId:this.sessionId}):this.removePortFile(),this.server?this.server.close(n=>{n?(r.error("Error stopping HTTP server:",n),i(n)):(r.info("HTTP API stopped"),t())}):t()})}};var Ie=require("child_process"),Ee=require("@quantiya/codevibe-core");var ie=class{async executePrompt(e,t){let i=(0,Ee.getConfig)(),n=i.claude.defaultTimeout;return r.info("Executing prompt from mobile",{sessionId:e,promptLength:t.length,timeout:n}),new Promise(s=>{let a=["--resume",e,"--print","--output-format","stream-json",t];r.debug("Spawning Claude command",{command:i.claude.command,args:a});let o=(0,Ie.spawn)(i.claude.command,a,{stdio:["pipe","pipe","pipe"],shell:!0}),c="",u="",m=!1,h=setTimeout(()=>{m=!0,r.warn("Command execution timed out",{sessionId:e,timeout:n}),o.kill("SIGTERM")},n);o.stdout?.on("data",l=>{let f=l.toString();c+=f,r.debug("Command stdout",{output:f.slice(0,200)})}),o.stderr?.on("data",l=>{let f=l.toString();u+=f,r.debug("Command stderr",{output:f.slice(0,200)})}),o.on("close",l=>{clearTimeout(h);let f={success:l===0&&!m,output:c,error:u,exitCode:l||void 0,timedOut:m};f.success?r.info("Command executed successfully",{sessionId:e,exitCode:l,outputLength:c.length}):r.error("Command execution failed",{sessionId:e,exitCode:l,timedOut:m,error:u.slice(0,500)}),s(f)}),o.on("error",l=>{clearTimeout(h),r.error("Failed to spawn command",{error:l.message}),s({success:!1,error:l.message,timedOut:!1})})})}detectInteractivePrompt(e){return[/\[Y\/n\]/i,/\[y\/N\]/i,/\(y\/n\)/i,/Continue\?/i,/Proceed\?/i].some(i=>i.test(e))}extractPromptText(e){let t=e.split(`
2
- `);for(let i=t.length-1;i>=0;i--){let n=t[i].trim();if(this.detectInteractivePrompt(n))return n}return null}};var Pe=require("child_process"),be=require("util");var ue=(0,be.promisify)(Pe.exec),re=class{async answerInteractivePrompt(e,t,i={}){let{pressEnter:n=!0}=i;r.info("Attempting to answer interactive prompt",{sessionId:e,response:t,pressEnter:n});try{let s=process.env.CODEVIBE_TMUX_SESSION;return r.info("Checking tmux session environment",{tmuxSession:s||"(not set)",allEnvKeys:Object.keys(process.env).filter(a=>a.includes("CODEVIBE")||a.includes("TMUX"))}),s?(r.info("Using tmux send-keys",{tmuxSession:s,pressEnter:n}),await this.sendViaTmux(s,t,n),r.info("Successfully sent response to interactive prompt",{sessionId:e,response:t,pressEnter:n}),!0):(r.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:e,hint:"Start Claude Code using `codevibe --agent claude`"}),!1)}catch(s){return r.error("Failed to answer interactive prompt",{sessionId:e,error:s instanceof Error?s.message:String(s)}),!1}}async sendViaTmux(e,t,i){let n=t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\$/g,"\\$").replace(/`/g,"\\`");r.info("Sending via tmux",{sessionName:e,inputLength:t.length,pressEnter:i});try{let s=`tmux send-keys -t "${e}" -l "${n}"`,a=await ue(s);if(r.info("tmux send-keys (text) completed",{stdout:a.stdout||"(empty)",stderr:a.stderr||"(empty)"}),i){await this.delay(500);let o=`tmux send-keys -t "${e}" Enter`,c=await ue(o);r.info("tmux send-keys (Enter) completed",{stdout:c.stdout||"(empty)",stderr:c.stderr||"(empty)"})}else r.info("tmux send-keys: skipping Enter (caller requested digit-only)")}catch(s){throw r.error("tmux send-keys failed",{sessionName:e,error:s}),s}}async sendKey(e,t){let i=process.env.CODEVIBE_TMUX_SESSION;if(!i)return r.error("No tmux session found for sendKey",{sessionId:e,keyName:t}),!1;try{let n=`tmux send-keys -t "${i}" ${t}`,s=await ue(n);return r.info("tmux send-keys (single key) completed",{sessionId:e,keyName:t,stdout:s.stdout||"(empty)",stderr:s.stderr||"(empty)"}),!0}catch(n){return r.error("tmux send-keys (single key) failed",{sessionId:e,keyName:t,error:n instanceof Error?n.message:String(n)}),!1}}delay(e){return new Promise(t=>setTimeout(t,e))}isPromptResponse(e){let t=e.trim().toLowerCase();return!!(t==="y"||t==="n"||t==="yes"||t==="no"||/^[0-9]+$/.test(t)||/^[a-z]$/.test(t)||["exit","quit","q","continue","skip","abort","retry","cancel"].includes(t))}};var K=require("@quantiya/codevibe-core"),ke=3e3,Be=8,qe=10,He=100;function Te(y){let e=Buffer.byteLength(y.carryover);return{status:"partial",progressKey:`${y.fileIdentity.dev}:${y.fileIdentity.ino}:${y.size}:${y.endOfComplete}:${e}`,size:y.size,carryoverBytes:e,endOfComplete:y.endOfComplete}}var We="tool_activity",se=class{constructor(e){this.consolidator=null;this.ledger=null;this.cursor=null;this.backendSessionId=null;this.transcriptPath=null;this.backstopTimer=null;this.backstopInFlight=null;this.deps=e}get sessionId(){return this.backendSessionId}isActive(){return this.consolidator!==null}async start(e,t){this.backendSessionId=e,this.transcriptPath=t??null;let i=new K.ToolActivityOutbox({agent:"claude",logger:this.deps.logger,root:this.deps.root});this.ledger=new K.ToolActivityLedger({agent:"claude",sessionId:e,logger:this.deps.logger,root:this.deps.root}),t&&(this.cursor=new K.ToolReplayCursor({agent:"claude",sessionId:e,canonicalPath:t,logger:this.deps.logger,root:this.deps.root,readBytes:this.deps.replayReadBytes,maxFrameBytes:this.deps.replayMaxFrameBytes}),await this.cursor.load()),this.consolidator=new K.ToolActivityConsolidator({agent:"claude",sessionId:e,logger:this.deps.logger,outbox:i,ledger:this.ledger,transport:this.buildTransport()}),await this.consolidator.init(),this.cursor&&await this.consolidator.repairLostWindows(this.cursor);let n=this.deps.backstopIntervalMs??3e3;this.cursor&&n>0&&(this.backstopTimer=setInterval(()=>{this.runTranscriptBackstop().catch(s=>this.deps.logger.warn("[tool-activity] transcript backstop failed",{sessionId:e,err:String(s)}))},n),this.backstopTimer.unref?.()),this.deps.logger.info("[tool-activity] consolidator started",{sessionId:e,hasTranscript:!!t})}async observeHookTool(e){if(!this.consolidator||!e.toolUseId||!e.toolName)return;let t=this.consolidator,i={id:e.toolUseId,tool:e.toolName,normalizedTarget:Ce(e.toolName,e.toolInput),ts:e.ts??new Date().toISOString(),byteOffset:0,...e.toolInput!==void 0?{digest:(0,K.digestOf)(e.toolInput)}:{}},n=await t.observe(i);return n==="closed"?await t.captureTerminal(i,this.deps.shutdownDrainMs??ke)?"sealed-terminal":"closed":n}async runTranscriptBackstop(){if(this.backstopInFlight)return this.backstopInFlight;if(!this.consolidator||!this.cursor)return;let e=this.doTranscriptBackstop().then(()=>{});return this.backstopInFlight=e,e.catch(()=>{}).finally(()=>{this.backstopInFlight===e&&(this.backstopInFlight=null)}),e}async doTranscriptBackstop(e=!1){let t=this.consolidator,i=this.cursor;if(!t||!i)return"caught-up";this.ledger&&await this.ledger.reloadLegacyOwned();for(let n=0;n<Be;n+=1){let s=await i.readFrames();if(s.missing)return"caught-up";if(s.frames.length===0)return s.carryover.length>0?Te(s):"caught-up";let a=Number.POSITIVE_INFINITY,o=e?{includeInFlight:!0}:void 0;for(let c of s.frames)for(let u of je(c.line,c.startOffset))await t.observe(u,o)==="deferred-in-flight"&&(a=Math.min(a,c.startOffset));if(await t.flush(),a<s.endOfComplete)return await i.checkpoint(a,{fileIdentity:s.fileIdentity,size:s.size,carryover:""}),"more";if(await i.checkpoint(s.endOfComplete,{fileIdentity:s.fileIdentity,size:s.size,carryover:s.carryover}),!s.hasMore)return s.carryover.length>0?Te(s):"caught-up"}return"more"}async flush(){await this.consolidator?.flush()}async stop(){this.backstopTimer&&(clearInterval(this.backstopTimer),this.backstopTimer=null),await this.runTranscriptBackstop().catch(()=>{});let e=Math.max(1,Math.trunc(this.deps.shutdownPartialNoProgressTurns??He)),t=null,i=0;for(;;){let n=await this.doTranscriptBackstop(!0);if(n==="caught-up")break;if(typeof n!="string"){if(n.progressKey===t?i+=1:(t=n.progressKey,i=1),i>=e){this.deps.logger.warn("[tool-activity] deferring unchanged incomplete transcript suffix during shutdown; the next owner will re-read it from the durable complete-line boundary",{size:n.size,carryoverBytes:n.carryoverBytes,endOfComplete:n.endOfComplete,unchangedTurns:i});break}await new Promise(s=>setTimeout(s,qe))}else t=null,i=0,await new Promise(s=>setImmediate(s))}await this.consolidator?.drainSends(this.deps.shutdownDrainMs??ke).catch(()=>{}),await this.consolidator?.shutdown(),this.consolidator=null,this.ledger=null,this.cursor=null}buildTransport(){let e=async(t,i,n)=>{let s=n?n.sessionKey:this.deps.getSessionKey();if(!s)throw new Error("[tool-activity] no session key at send (retryable)");let a=n?n.sessionKeyGen:this.deps.getSessionKeyGen();await this.deps.createEvent({sessionId:t,type:K.EventType.TOOL_ACTIVITY,source:K.EventSource.DESKTOP,content:this.deps.encryptContent(We,s),metadata:{encrypted:this.deps.encryptMetadata({manifest:i.manifest},s)},timestamp:i.windowStart,isEncrypted:!0,clientEventId:i.clientEventId,...a?{expectedSessionKeyGen:a}:{}})};return{send:async(t,i)=>{try{await e(t,i)}catch(n){if((0,K.isSessionKeyStaleError)(n)&&this.deps.refreshSessionKey){let s=await this.deps.refreshSessionKey(t);if(!s)throw n;await e(t,i,s);return}throw n}}}}};function je(y,e){let t;try{t=JSON.parse(y)}catch{return[]}let i=t?.message?.content;if(t?.type!=="assistant"||!Array.isArray(i))return[];let n=[];for(let s of i)s?.type==="tool_use"&&typeof s.id=="string"&&typeof s.name=="string"&&n.push({id:s.id,tool:s.name,normalizedTarget:Ce(s.name,s.input),ts:typeof t.timestamp=="string"?t.timestamp:new Date(0).toISOString(),byteOffset:e,...s.input!==void 0?{digest:(0,K.digestOf)(s.input)}:{}});return n}function Ce(y,e){if(!e||typeof e!="object")return;let t=e,i=n=>typeof n=="string"&&n.length>0?n:void 0;return i(t.file_path)??i(t.path)??i(t.pattern)??i(t.notebook_path)??i(t.url)??(typeof t.command=="string"?t.command.trim().split(/\s+/)[0]:void 0)}var oe=(0,Ae.promisify)(xe.exec),ze="/exit",J="CODEVIBE_TMUX_SESSION",Ye=1800*1e3,Xe=60*1e3,Je=1440*60*1e3;async function Ze(y,e){let t=async(i,n)=>{try{await oe(i)}catch(s){r.warn("tmux send-keys failed during self-terminate",{sessionName:y,label:n,error:String(s)})}};await t(`tmux send-keys -t "${y}" C-c`,"ctrl-c"),await new Promise(i=>setTimeout(i,200)),await t(`tmux send-keys -t "${y}" -l "${e}"`,"quit-text"),await new Promise(i=>setTimeout(i,500)),await t(`tmux send-keys -t "${y}" Enter`,"enter")}var et={question:"Ready to submit your answers?",options:[{label:"Submit answers",description:"Send your selections to the assistant"},{label:"Cancel",description:"Discard your answers"}],multiSelect:!1,_isSubmit:!0};function tt(y){let e=y.length,t=e>=2,i=y.slice(1);return t&&i.push({...et}),{questionCount:e,hasReviewScreen:t,remainingQueue:i}}var ce=class y{constructor(e){this.activeSessions=new Map;this.assignedPort=0;this.portRefreshTimer=null;this.e1TtlRefreshTimer=null;this.encryptedKeyRecoveryTimer=null;this.encryptedKeyRecoveryAttempt=0;this.encryptedKeyRecovery=null;this.sessionKey=null;this.sessionKeyGen=null;this.sessionIsEncrypted=!1;this.encryptionResolved=!0;this.toolActivity=null;this.toolActivityLegacyRecorders=new Map;this.claudeToBackendSessionId=new Map;this.sessionStartChain=Promise.resolve();this.sessionLifecycleQueueDepth=0;this.retiredSessionRecoveryPromises=new Map;this.lifecycleGeneration=0;this.isStopping=!1;this.currentClaudeSessionId=null;this.claudeOwnershipGeneration=0;this.claudeSessionIntentVersions=new Map;this.endedOrSupersededClaudeSessionIds=new Set;this.nestedClaudeSessionIds=new Set;this.pendingMobilePrompts=new Map;this.e1PromptRaises=new Map;this.e1NeedsReRaise=new Set;this.e1ParkedOrphans=new Map;this.nextPromptGen=1;this.httpApi=new ne(()=>this.getDaemonHealth()),this.commandExecutor=new ie,this.promptResponder=new re,this.initialSessionId=e,this.currentClaudeSessionId=e??null}static{this.E1_TTL_REFRESH_MS=300*1e3}static{this.MOBILE_PROMPT_EXPIRY_MS=3e3}newE1Raise(e,t,i){let{record:n}=(0,d.newPromptRaise)({sessionId:e,producerKind:"PLUGIN_APPROVAL",mobileActionable:t,agentType:"CLAUDE",...i!==void 0&&{title:i}});return this.e1PromptRaises.set(n.promptId,{record:n}),n}stashE1RaiseContent(e,t,i){let n=this.e1PromptRaises.get(e);n&&(n.contentPlain=t,n.metadataPlain=i)}snapshotOpenE1(e){return[...this.e1PromptRaises.values()].filter(t=>t.record.sessionId===e).map(t=>({record:{...t.record},...t.contentPlain!==void 0&&{contentPlain:t.contentPlain},...t.metadataPlain!==void 0&&{metadataPlain:t.metadataPlain}}))}async reRaiseSnapshotE1(e,t,i){if(e.length===0)return[];r.info("E1: re-raising open prompts onto replacement session",{replacementSessionId:t,count:e.length});let n=[];for(let s of e){let a={record:{...s.record,sessionId:t},...s.contentPlain!==void 0&&{contentPlain:s.contentPlain},...s.metadataPlain!==void 0&&{metadataPlain:s.metadataPlain}};this.e1PromptRaises.set(a.record.promptId,a),await this.reRaiseOneE1(t,a,i?{untilAck:i}:void 0)||n.push(s)}return n}parkE1Orphans(e,t){if(t.length===0)return;let i=new Map;for(let n of this.e1ParkedOrphans.get(e)??[])i.set(n.record.promptId,n);for(let n of t)i.set(n.record.promptId,n);this.e1ParkedOrphans.set(e,[...i.values()]),r.info("E1: parked retired-session open prompts for replacement re-home",{claudeSessionId:e,count:i.size})}async drainParkedE1Orphans(e,t,i){let n=this.e1ParkedOrphans.get(e);if(!n||n.length===0)return;let s=await this.reRaiseSnapshotE1(n,t,i);if(s.length===0){this.e1ParkedOrphans.delete(e);return}this.e1ParkedOrphans.set(e,s),r.info("E1: retirement drain aborted before ACK \u2014 retained durable park for next bootstrap",{claudeSessionId:e,liveSessionId:t,retained:s.length})}async reRaiseOneE1(e,t,i){let n=t.record,s=n.mobileActionable&&t.contentPlain!==void 0,a=i?.untilAck,o=4;for(let c=1;;c++){if(a&&!a())return this.e1NeedsReRaise.add(n.promptId),r.info("E1: re-raise wait aborted (lifecycle superseded/stopping) \u2014 flagged for TTL-drain",{sessionId:e,promptId:n.promptId}),!1;try{if(s){let u=this.encryptForEmit(t.contentPlain,t.metadataPlain??{},d.EventType.INTERACTIVE_PROMPT,e);if(!u)return!1;await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.INTERACTIVE_PROMPT,source:d.EventSource.DESKTOP,content:u.content,metadata:u.metadata,...(0,d.raiseFieldsFromRecord)(n),timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:u.isEncrypted?!0:void 0})}else if(n.mobileActionable=!1,!await this.emitSuppressedPromptCarrier(e,n))return!1;return this.e1NeedsReRaise.delete(n.promptId),r.info("E1: re-raised prompt onto replacement session",{sessionId:e,promptId:n.promptId,actionable:s}),!0}catch(u){if(!a&&c>=o)return this.e1NeedsReRaise.add(n.promptId),r.error("E1: re-raise failed after retries \u2014 flagged for TTL-drain re-attempt",{sessionId:e,promptId:n.promptId,error:u instanceof Error?u.message:String(u)}),!1;if(a){let m=Date.now()+Math.min(250*2**(c-1),4e3);for(;Date.now()<m&&a();)await new Promise(h=>setTimeout(h,Math.min(50,m-Date.now())))}else await new Promise(m=>setTimeout(m,250*c))}}}async drainPendingE1ReRaises(){for(let e of[...this.e1NeedsReRaise]){let t=this.e1PromptRaises.get(e);if(!t){this.e1NeedsReRaise.delete(e);continue}this.activeSessions.has(t.record.sessionId)&&await this.reRaiseOneE1(t.record.sessionId,t)}}getDaemonHealth(){let e=this.encryptedKeyRecovery;return e?{status:"degraded",reason:"encrypted_session_key_unavailable",sessionId:e.backendSessionId,retryAttempt:this.encryptedKeyRecoveryAttempt}:{status:"healthy"}}clearEncryptedKeyRecovery(e,t,i){t&&this.encryptedKeyRecovery?.claudeSessionId!==t||i&&this.encryptedKeyRecovery?.backendSessionId!==i||(this.encryptedKeyRecoveryTimer&&(clearTimeout(this.encryptedKeyRecoveryTimer),this.encryptedKeyRecoveryTimer=null),this.encryptedKeyRecovery&&r.info("Encrypted-session key recovery cleared",{claudeSessionId:this.encryptedKeyRecovery.claudeSessionId,backendSessionId:this.encryptedKeyRecovery.backendSessionId,reason:e}),this.encryptedKeyRecovery=null,this.encryptedKeyRecoveryAttempt=0)}enterEncryptedKeyRecovery(e,t){let i=e.session_id,n=this.encryptedKeyRecovery;return!n||n.claudeSessionId!==i||n.backendSessionId!==t?(this.clearEncryptedKeyRecovery("replaced by a different recovery target"),this.encryptedKeyRecovery={claudeSessionId:i,backendSessionId:t,payload:e,livenessInvalidated:!1}):(n.payload=e,n.livenessInvalidated=!1,this.encryptedKeyRecoveryTimer&&(clearTimeout(this.encryptedKeyRecoveryTimer),this.encryptedKeyRecoveryTimer=null)),this.encryptedKeyRecovery}async invalidateEncryptedRecoveryLiveness(e){for(;!this.isStopping&&this.encryptedKeyRecovery===e;){let t=await this.appSyncClient.invalidateHeartbeat(e.backendSessionId);if(this.isStopping||this.encryptedKeyRecovery!==e)return!1;if(t)return e.livenessInvalidated=!0,this.scheduleEncryptedKeyRecovery(e.payload,e.backendSessionId),!0;r.warn("Retrying durable invalidation before encrypted-key recovery",{claudeSessionId:e.claudeSessionId,backendSessionId:e.backendSessionId}),await new Promise(i=>setTimeout(i,1e3))}return!1}scheduleEncryptedKeyRecovery(e,t){if(this.isStopping)return;let i=e.session_id,n=this.encryptedKeyRecovery;if(!n||n.claudeSessionId!==i||n.backendSessionId!==t){let o=this.enterEncryptedKeyRecovery(e,t);this.invalidateEncryptedRecoveryLiveness(o);return}else n.payload=e;if(!n.livenessInvalidated||this.encryptedKeyRecoveryTimer)return;let s=Math.min(6e4,5e3*2**Math.min(this.encryptedKeyRecoveryAttempt,4));this.encryptedKeyRecoveryAttempt+=1;let a=this.encryptedKeyRecovery;a&&(r.warn("Encrypted session is keyless; scheduling automatic resume recovery",{claudeSessionId:i,backendSessionId:t,attempt:this.encryptedKeyRecoveryAttempt,delayMs:s}),this.encryptedKeyRecoveryTimer=setTimeout(()=>{this.encryptedKeyRecoveryTimer=null,!(this.isStopping||this.encryptedKeyRecovery!==a)&&this.handleSessionStart(a.payload,a.backendSessionId,"self-heal").catch(o=>{r.warn("Encrypted-session key recovery attempt failed",{claudeSessionId:i,backendSessionId:t,error:String(o)})}).finally(()=>{!this.isStopping&&this.encryptedKeyRecovery===a&&!this.encryptedKeyRecoveryTimer&&this.scheduleEncryptedKeyRecovery(a.payload,a.backendSessionId)})},s),this.encryptedKeyRecoveryTimer.unref?.())}getPort(){return this.assignedPort}generateBackendSessionId(e){return`claude-${e}`}trackMobilePrompt(e,t){this.pendingMobilePrompts.has(e)||this.pendingMobilePrompts.set(e,[]),this.pendingMobilePrompts.get(e).push({prompt:t.trim(),timestamp:Date.now()}),r.debug("Tracking mobile prompt for deduplication",{sessionId:e,promptLength:t.length})}isRecentMobilePrompt(e,t){let i=this.pendingMobilePrompts.get(e);if(!i)return!1;let n=Date.now(),s=t.trim(),a=[],o=!1;for(let c of i)if(!(n-c.timestamp>y.MOBILE_PROMPT_EXPIRY_MS)){if(!o&&c.prompt===s){o=!0,r.debug("Found matching mobile prompt, filtering duplicate",{sessionId:e});continue}a.push(c)}return a.length>0?this.pendingMobilePrompts.set(e,a):this.pendingMobilePrompts.delete(e),o}writePortFile(e){let t=G.join(W.tmpdir(),`codevibe-claude-${e}.port`);try{D.writeFileSync(t,this.assignedPort.toString()),r.info(`Port file written: ${t} -> ${this.assignedPort}`)}catch(i){r.error(`Failed to write port file: ${t}`,i)}}removePortFile(e){let t=G.join(W.tmpdir(),`codevibe-claude-${e}.port`);try{D.existsSync(t)&&(D.unlinkSync(t),r.info(`Port file removed: ${t}`))}catch(i){r.warn(`Failed to remove port file: ${t}`,i)}}hasOtherLiveDaemonForSession(e){try{let t=(0,Re.execSync)("ps -eww -o pid= -o args=",{encoding:"utf8",timeout:2e3}),i=process.pid;for(let n of t.split(`
3
- `)){let s=n.trim();if(!s)continue;let a=s.indexOf(" ");if(a<0)continue;let o=parseInt(s.substring(0,a),10);if(isNaN(o)||o===i)continue;let c=s.substring(a+1);if(/node.*codevibe-claude.*server\.js/.test(c)&&c.includes(e))return!0}return!1}catch(t){return r.warn('hasOtherLiveDaemonForSession: ps query failed; falling back to "no other daemon"',{error:String(t)}),!1}}async start(){try{if(r.info("Starting CodeVibe MCP Server...",{environment:(0,d.getEnvironment)()}),this.appSyncClient=new d.AppSyncClient,await this.appSyncClient.authenticateWithStoredTokens()){r.info("Authenticated with stored OAuth tokens",{userId:this.appSyncClient.getCurrentUserId(),email:this.appSyncClient.getCurrentUserEmail()}),await(0,d.registerDeviceEncryptionKey)(this.appSyncClient,r),(0,d.startDeviceKeyWatcher)(this.appSyncClient,r),(0,d.pushDetectedAgents)(this.appSyncClient,r).catch(t=>{r.warn("Failed to update available agents (non-fatal)",{error:t?.message})});try{let t=await this.appSyncClient.sweepOrphanSessions({agentType:"CLAUDE"});t>0&&r.info("Orphan sweep: marked stale Claude sessions INACTIVE",{swept:t})}catch(t){r.warn("Orphan sweep failed, continuing startup",{error:t instanceof Error?t.message:String(t)})}}else r.error('Authentication failed. Run "codevibe login" first.'),console.error('Not authenticated. Run "codevibe login" to sign in.'),process.exit(1);this.httpApi.onEvent(this.handleEventFromHook.bind(this)),this.assignedPort=await this.httpApi.start(this.initialSessionId),this.startPortKeepalive(),r.info("MCP Server started successfully",{port:this.assignedPort,host:(0,d.getConfig)().server.host,dynamicPort:(0,d.getConfig)().server.dynamicPort,sessionId:this.initialSessionId,authenticated:this.appSyncClient.isAuthenticated(),userId:this.appSyncClient.getCurrentUserId()})}catch(e){throw r.error("Failed to start MCP Server:",e),e}}startPortKeepalive(){this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null);let e=Number(process.env.CODEVIBE_PORTFILE_REFRESH_MS),t=Number.isFinite(e)&&e>0?Math.min(Je,Math.max(Xe,e)):Ye;this.portRefreshTimer=setInterval(()=>this.refreshPortFiles(),t),this.e1TtlRefreshTimer&&clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=setInterval(()=>{this.refreshE1Ttls()},y.E1_TTL_REFRESH_MS)}async refreshE1Ttls(){await this.drainPendingE1ReRaises();let e=[...this.e1PromptRaises.values()].filter(t=>this.activeSessions.has(t.record.sessionId)).map(t=>t.record.promptId);if(e.length!==0)try{let t=await this.appSyncClient.refreshOpenPromptTtl(e);for(let i of t){let n=this.e1PromptRaises.get(i);n&&this.activeSessions.has(n.record.sessionId)&&await this.reRaiseOneE1(n.record.sessionId,n)}}catch(t){r.warn("E1: TTL-refresh tick failed (non-fatal, retries next tick)",{error:t instanceof Error?t.message:String(t)})}}refreshPortFiles(){let e=new Date;for(let i of this.activeSessions.values()){let n=G.join(W.tmpdir(),`codevibe-claude-${i.claudeSessionId}.port`);try{D.utimesSync(n,e,e)}catch(a){a?.code==="ENOENT"?this.writePortFile(i.claudeSessionId):r.warn(`Port-file refresh failed: ${n}`,a)}let s=G.join(W.tmpdir(),`codevibe-claude-${i.claudeSessionId}.pid`);try{D.utimesSync(s,e,e)}catch(a){a?.code!=="ENOENT"&&r.warn(`Pid-file refresh failed: ${s}`,a)}}let t=process.env[J];if(t){let i=G.join(W.tmpdir(),`codevibe-claude-instance-${t}.json`);try{D.utimesSync(i,e,e)}catch(n){n?.code!=="ENOENT"&&r.warn(`Instance-map refresh failed: ${i}`,n)}}}async stop(){r.info("Stopping MCP Server..."),this.isStopping=!0,this.lifecycleGeneration+=1,this.clearEncryptedKeyRecovery("daemon stopping"),await this.sessionStartChain.catch(()=>{}),this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null),this.e1TtlRefreshTimer&&(clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=null),this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null);let e=Array.from(this.activeSessions.keys()),t=new Set;r.info(`Marking ${e.length} active session(s) as INACTIVE...`);for(let i of e){let n=this.activeSessions.get(i);n?.mobileEndWatcher&&(n.mobileEndWatcher.stop(),n.mobileEndWatcher=void 0),this.appSyncClient.stopHeartbeat(i)}for(let i of e){let n=this.activeSessions.get(i);try{if(n&&this.hasOtherLiveDaemonForSession(n.claudeSessionId)){r.info("Another daemon serves this session \u2014 skipping mark INACTIVE AND port file removal during shutdown",{sessionId:i,claudeSessionId:n.claudeSessionId,myPid:process.pid}),t.add(n.claudeSessionId);continue}try{await this.appSyncClient.updateSession({sessionId:i,status:d.SessionStatus.INACTIVE}),r.info("Session marked as INACTIVE during shutdown",{sessionId:i})}catch(s){r.warn("Failed to mark session as INACTIVE during shutdown",{sessionId:i,error:s})}n&&this.removePortFile(n.claudeSessionId)}catch(s){r.warn("Failed during shutdown cleanup",{sessionId:i,error:s})}}this.appSyncClient.cleanupSubscriptions(),this.activeSessions.clear(),await this.httpApi.stop({protectedSessionIds:t}),r.info("MCP Server stopped")}encryptForEmit(e,t,i,n){let s=this.activeSessions.get(n)?.encryptionSnapshot;if(!s)return r.error("No encryption snapshot for target session \u2014 dropping event (fail-closed, #638 H1)",{type:i,sessionId:n,sessionKnown:this.activeSessions.has(n)}),null;let a=(0,d.encryptForEmit)(s.sessionKey,s.sessionIsEncrypted,e,t,s.encryptionResolved);return a===null&&r.error("No session key for ENCRYPTED/unresolved session \u2014 dropping event (fail-closed, #638)",{type:i,sessionId:n,encryptionResolved:s.encryptionResolved}),a}async handleEventFromHook(e){let{session_id:t,hook_event_name:i,type:n,content:s}=e;if(r.info("Processing hook event",{sessionId:t,hookEvent:i,type:n}),i==="SessionStart"&&e.metadata?.source==="startup"&&this.currentClaudeSessionId!==null&&this.currentClaudeSessionId!==t&&this.nestedClaudeSessionIds.add(t),this.nestedClaudeSessionIds.has(t)){r.info("Ignoring hook from quarantined nested Claude session",{sessionId:t,hookEvent:i,owningSessionId:this.currentClaudeSessionId});return}let a,o;try{if(i==="SessionStart")await this.handleSessionStart(e);else if(i==="SessionEnd")await this.handleSessionEnd(e);else if(!this.activeSessions.has(this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t))){r.warn("Hook event for un-bootstrapped session \u2014 self-healing via SessionStart (#638)",{claudeSessionId:t,hookEvent:i});let p=this.encryptedKeyRecovery?.claudeSessionId===t?this.encryptedKeyRecovery.backendSessionId:void 0;await this.handleSessionStart({session_id:t,metadata:e.metadata,transcript_path:e.transcript_path},p,"self-heal");let S=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);if(!this.activeSessions.has(S)){r.warn("Dropping hook because Claude self-heal lifecycle intent is stale",{claudeSessionId:t,hookEvent:i});return}}let c=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);if(this.toolActivity?.isActive()&&(n===d.EventType.USER_PROMPT||n===d.EventType.ASSISTANT_RESPONSE||n===d.EventType.INTERACTIVE_PROMPT||n===d.EventType.NOTIFICATION)&&await this.toolActivity.flush().catch(p=>r.warn("[tool-activity] boundary flush failed",{sessionId:c,err:String(p)})),i==="UserPromptSubmit"){let p=this.activeSessions.get(c);if(p?.completedAskUserQuestionFingerprints?.size){let S=p.completedAskUserQuestionFingerprints.size;p.completedAskUserQuestionFingerprints.clear(),r.info("Turn boundary \u2014 cleared closed-AskUserQuestion fingerprints",{sessionId:c,clearedCount:S})}}if(n===d.EventType.USER_PROMPT&&e.source===d.EventSource.DESKTOP&&i==="UserPromptSubmit"&&s&&this.isRecentMobilePrompt(c,s)){r.info("Skipping duplicate USER_PROMPT from mobile-originated prompt",{sessionId:c,contentLength:s.length});return}if(n===d.EventType.INTERACTIVE_PROMPT){(typeof e.prompt_id!="string"||e.prompt_id.length===0)&&(e.prompt_id=`synth-${(0,j.randomUUID)()}`,r.info("Synthesized prompt_id for INTERACTIVE_PROMPT (hook omitted it)",{sessionId:c,synthesizedPromptId:e.prompt_id}));let p=this.activeSessions.get(c),S;if(p&&e.metadata?.tool_name==="AskUserQuestion"){if(S=this.computeAskUserQuestionFingerprint(e.metadata.tool_input?.questions),S){let C=p.activeAskUserQuestionFingerprint===S,b=p.completedAskUserQuestionFingerprints?.has(S)??!1;if(C||b){r.info("Dropping duplicate INTERACTIVE_PROMPT \u2014 AskUserQuestion already tracked",{sessionId:c,fingerprint:S.slice(0,16),status:C?"in-flight":"completed",hookEvent:e.hook_event_name,promptId:e.prompt_id});return}}let N=e.metadata.tool_input?.questions,w=Array.isArray(N)&&N.some(C=>C?.multiSelect===!0),v=Array.isArray(N)&&(N[0]?.options?.length??0)===0;if(S&&(w||v)){p.completedAskUserQuestionFingerprints||(p.completedAskUserQuestionFingerprints=new Set),p.completedAskUserQuestionFingerprints.add(S);let C=w?"\u26A0\uFE0F This AskUserQuestion uses multi-select, which can't be answered from mobile. Please answer it on your desktop terminal.":"\u26A0\uFE0F This AskUserQuestion has no explicit options \u2014 please answer on your desktop terminal.";r.info("AUQ degraded at intercept \u2014 emitting notification, skipping walker setup",{sessionId:c,fingerprint:S.slice(0,16),hasMultiSelect:w,isZeroExplicitOption:v,questionCount:Array.isArray(N)?N.length:0}),setImmediate(()=>this.emitDegradedAUQNotification(c,C));let b=this.newE1Raise(c,!1);this.emitSuppressedPromptCarrier(c,b).catch(O=>{r.error("E1: failed to emit degraded-AUQ badge carrier",{sessionId:c,promptId:b.promptId,error:O instanceof Error?O.message:String(O)})});return}}if(p){let N=this.newE1Raise(c,!0);e.prompt_id=N.promptId,this.clearPromptState(p),p.waitingForPromptResponse=!0,p.pendingPromptId=e.prompt_id;let w=this.nextPromptGen++;p.promptGenerationToken={promptId:e.prompt_id||"",gen:w},S&&(p.activeAskUserQuestionFingerprint=S),r.info("Interactive prompt detected - will parse options from tmux",{sessionId:c,promptId:e.prompt_id,tokenGen:w,askUserQuestionFingerprint:S?.slice(0,16)})}this.sendInteractivePromptAsync(c,e,s).catch(N=>{r.error("Failed to send interactive prompt with dynamic options",{error:N})});return}if(n===d.EventType.TOOL_USE&&this.toolActivity?.isActive()&&await this.toolActivity.observeHookTool({toolUseId:e.tool_use_id,toolName:e.metadata?.tool_name,toolInput:e.tool_input})!=="closed")return;n===d.EventType.TOOL_USE&&e.tool_use_id&&this.sessionKey&&(o=String(e.tool_use_id),a=this.getToolActivityLegacyRecorder(c),a.markLegacyInFlight(o)),r.info("Hook event encryption state",{type:n,sessionId:c,hasSessionKey:!!this.sessionKey,sessionKeyLength:this.sessionKey?.length||0});let m=this.encryptForEmit(s,e.metadata,n,c);if(!m)return;let h=m.content,l=m.metadata,f=m.isEncrypted,g=await this.appSyncClient.createEvent({sessionId:c,type:n,source:e.source,content:h,metadata:l,promptId:e.prompt_id,timestamp:(0,d.prepareEventTimestamp)({orderingKey:c}),isEncrypted:f?!0:void 0});if(a&&o&&((0,d.isPersistedEventResult)(g)?await a.finalizeLegacyOwned([o]):a.clearLegacyInFlight(o)),n===d.EventType.USER_PROMPT&&e.source===d.EventSource.DESKTOP){let p=this.activeSessions.get(c);p?.waitingForPromptResponse&&(this.promoteFingerprintAndClearPromptState(p),r.info("Clearing prompt wait state - new desktop prompt received",{sessionId:c}))}r.debug("Event sent to AppSync successfully")}catch(c){throw a&&o&&a.clearLegacyInFlight(o),r.error("Failed to process hook event:",c),c}}buildToolActivityIntegration(){return new se({logger:r,createEvent:e=>this.appSyncClient.createEvent(e),encryptContent:(e,t)=>d.cryptoService.encryptContent(e,t),encryptMetadata:(e,t)=>d.cryptoService.encryptMetadata(e,t),getSessionKey:()=>this.sessionKey,getSessionKeyGen:()=>this.sessionKeyGen,refreshSessionKey:e=>this.refreshToolActivitySessionKey(e)})}async refreshToolActivitySessionKey(e){try{let t=await this.appSyncClient.getSession(e);if(t&&t.status===d.SessionStatus.INACTIVE){let a=new Error(`SESSION_TERMINAL: session ${e} is ${t.status}; stop retrying`);throw a.errorType="SessionTerminal",a}if(!t||t.status!==d.SessionStatus.ACTIVE||!t.isEncrypted)return null;let i=t.sessionKeyGen??null,n=t.encryptedKeys??[];if(n.length===0)return null;let s=await d.keychainManager.getSessionKey(e,n,i);return s?(r.info("[tool-activity] refreshed session key for SessionKeyStale retry (live key untouched)",{sessionId:e,sessionKeyGen:i}),{sessionKey:s,sessionKeyGen:i}):null}catch(t){if((0,d.isTerminalSessionError)(t))throw t;return r.warn("[tool-activity] session key refresh failed after SessionKeyStale (staying retryable)",{sessionId:e,error:String(t)}),null}}getToolActivityLegacyRecorder(e){let t=this.toolActivityLegacyRecorders.get(e);return t||(t=new d.ToolActivityLedger({agent:"claude",sessionId:e,logger:r}),this.toolActivityLegacyRecorders.set(e,t)),t}handleSessionStart(e,t,i="explicit"){if(this.isStopping)return Promise.resolve();let n=e.session_id,s=i==="explicit"?this.recordExplicitClaudeSessionStart(n):{ownershipGeneration:this.claudeOwnershipGeneration,sessionIntentVersion:this.getClaudeSessionIntentVersion(n)};return this.enqueueSessionLifecycle(async()=>{if(this.isStopping)return;if(i==="self-heal"){if(!this.canSelfHealClaudeSession(n,s)){r.warn("Rejected stale Claude self-heal request",{claudeSessionId:n,currentClaudeSessionId:this.currentClaudeSessionId});return}this.currentClaudeSessionId===null&&(this.currentClaudeSessionId=n,s.ownershipGeneration=++this.claudeOwnershipGeneration)}else if(!this.isClaudeStartIntentCurrent(n,s))return;let a=++this.lifecycleGeneration;await this.handleSessionStartSerialized(e,t,a,()=>this.isClaudeStartIntentCurrent(n,s))})}getClaudeSessionIntentVersion(e){return this.claudeSessionIntentVersions.get(e)??0}bumpClaudeSessionIntentVersion(e){let t=this.getClaudeSessionIntentVersion(e)+1;return this.claudeSessionIntentVersions.set(e,t),t}recordExplicitClaudeSessionStart(e){this.clearEncryptedKeyRecovery("explicit Claude SessionStart");let t=this.currentClaudeSessionId;t&&t!==e&&this.endedOrSupersededClaudeSessionIds.add(t);for(let i of this.activeSessions.values())i.claudeSessionId!==e&&this.endedOrSupersededClaudeSessionIds.add(i.claudeSessionId);return this.currentClaudeSessionId=e,this.endedOrSupersededClaudeSessionIds.delete(e),{ownershipGeneration:++this.claudeOwnershipGeneration,sessionIntentVersion:this.bumpClaudeSessionIntentVersion(e)}}recordExplicitClaudeSessionEnd(e){this.clearEncryptedKeyRecovery("explicit Claude SessionEnd",e),this.endedOrSupersededClaudeSessionIds.add(e),this.bumpClaudeSessionIntentVersion(e),this.currentClaudeSessionId===e&&(this.currentClaudeSessionId=null,this.claudeOwnershipGeneration+=1)}isClaudeStartIntentCurrent(e,t){return!this.isStopping&&this.currentClaudeSessionId===e&&this.claudeOwnershipGeneration===t.ownershipGeneration&&this.getClaudeSessionIntentVersion(e)===t.sessionIntentVersion&&!this.endedOrSupersededClaudeSessionIds.has(e)}canSelfHealClaudeSession(e,t){return this.endedOrSupersededClaudeSessionIds.has(e)||this.getClaudeSessionIntentVersion(e)!==t.sessionIntentVersion||this.claudeOwnershipGeneration!==t.ownershipGeneration||this.currentClaudeSessionId&&this.currentClaudeSessionId!==e?!1:!Array.from(this.activeSessions.values()).some(i=>i.claudeSessionId!==e)}enqueueSessionLifecycle(e){let t=this.sessionStartChain;this.sessionLifecycleQueueDepth+=1;let i=this.sessionLifecycleQueueDepth===1?e():t.catch(()=>{}).then(e),n=i.finally(()=>{this.sessionLifecycleQueueDepth-=1});return this.sessionStartChain=n.catch(()=>{}),i}async handleSessionStartSerialized(e,t,i=this.lifecycleGeneration,n=()=>!0){let s=()=>this.isLifecycleCurrent(i)&&n();if(!s())return;let a=e.session_id,o=t??this.generateBackendSessionId(a),c=e.metadata?.cwd||process.cwd();this.claudeToBackendSessionId.set(a,o),r.info("Session started",{claudeSessionId:a,sessionId:o,cwd:c});let u=Array.from(this.activeSessions.keys()).filter(g=>g!==o);if(u.length>0){r.info(`Marking ${u.length} previous session(s) as INACTIVE`);for(let g of u){let p=this.activeSessions.get(g);if(p?.mobileEndWatcher&&(p.mobileEndWatcher.stop(),p.mobileEndWatcher=void 0),p?.orchestrationPromptWatcher&&(p.orchestrationPromptWatcher.stop(),p.orchestrationPromptWatcher=void 0),p?.orchestrationDecisionWatcher&&(p.orchestrationDecisionWatcher.stop(),p.orchestrationDecisionWatcher=void 0),p?.orchestrationClient){try{await p.orchestrationClient.destroy()}catch(S){r.warn("[v1-bridge] OrchestrationClient.destroy() failed on resume",{prevId:g,error:String(S)})}p.orchestrationClient=void 0}this.appSyncClient.stopHeartbeat(g),this.appSyncClient.cleanupSubscription(g);try{await this.appSyncClient.updateSession({sessionId:g,status:d.SessionStatus.INACTIVE}),r.info("Previous session marked INACTIVE",{prevId:g,newSessionId:o})}catch(S){r.warn("Failed to mark previous session as INACTIVE",{prevId:g,error:S})}p&&this.removePortFile(p.claudeSessionId),this.activeSessions.delete(g)}}this.writePortFile(a);let m=this.appSyncClient.getCurrentUserId(),h={sessionId:o,claudeSessionId:a,userId:m,projectPath:c,cwd:c,createdAt:new Date,subscriptionActive:!1,waitingForPromptResponse:!1,metadata:e.metadata||{},transcriptPath:e.transcript_path,originatedDecisions:new Set,externallyResolvedGateIds:new Set};this.encryptionResolved=!1,h.encryptionSnapshot={sessionKey:null,sessionIsEncrypted:!1,encryptionResolved:!1},this.activeSessions.set(o,h);try{let g=await(0,d.resumeOrCreateSession)({sessionId:o,userId:h.userId,agentType:d.AgentType.CLAUDE,projectPath:c,metadata:e.metadata||{}},this.appSyncClient,r);if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:g.sessionId,claudeSessionId:a,sessionState:h});return}if(this.activeSessions.get(o)!==h){r.warn("Ignoring superseded Claude session bootstrap completion",{claudeSessionId:a,requestedSessionId:o,authoritativeSessionId:g.sessionId}),await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:g.sessionId,claudeSessionId:a,sessionState:h});return}if(g.sessionId!==o&&(this.activeSessions.get(o)===h&&this.activeSessions.delete(o),o=g.sessionId,h.sessionId=o,this.activeSessions.set(o,h),this.claudeToBackendSessionId.set(a,o),r.info("Bound Claude session to recovered backend identity",{claudeSessionId:a,sessionId:o})),this.sessionKey=g.sessionKey,this.sessionKeyGen=g.sessionKeyGen,this.sessionIsEncrypted=!!g.sessionKey,this.encryptionResolved=!0,h.encryptionSnapshot={sessionKey:g.sessionKey,sessionIsEncrypted:!!g.sessionKey,encryptionResolved:!0},this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null,!s())){await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:g.sessionId,claudeSessionId:a,sessionState:h});return}if(this.sessionKey){let p=this.buildToolActivityIntegration();try{if(await p.start(o,e.transcript_path),!s()){await p.stop().catch(()=>{}),await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:g.sessionId,claudeSessionId:a,sessionState:h});return}this.toolActivity=p}catch(S){r.warn("[tool-activity] consolidator start failed \u2014 keeping per-tool TOOL_USE",{sessionId:o,error:String(S)}),this.toolActivity=null,await p.stop().catch(()=>{})}}}catch(g){if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:this.authoritativeSessionIdFromError(g)??o,claudeSessionId:a,sessionState:h});return}let p=g?.authoritativeSessionId;if(typeof p=="string"&&p.length>0&&p!==o&&this.activeSessions.get(o)===h&&(this.activeSessions.delete(o),o=p,h.sessionId=o,this.activeSessions.set(o,h),this.claudeToBackendSessionId.set(a,o),r.info("Bound keyless Claude recovery to authoritative backend identity",{claudeSessionId:a,sessionId:o})),this.isSessionLimitExceeded(g)){this.displaySubscriptionLimitError(g,"session"),this.activeSessions.get(o)===h&&this.activeSessions.delete(o),this.removePortFile(a);return}if(g?.code==="ENCRYPTED_SESSION_NO_KEY"){this.sessionIsEncrypted=!0,this.sessionKey=null,this.encryptionResolved=!0,h.encryptionSnapshot={sessionKey:null,sessionIsEncrypted:!0,encryptionResolved:!0};let S=this.enterEncryptedKeyRecovery(e,o);this.appSyncClient.cleanupSubscription(o),await this.invalidateEncryptedRecoveryLiveness(S),r.error("Failed to create/resume session:",g);return}else{this.activeSessions.get(o)===h&&this.activeSessions.delete(o),r.error("Failed to create/resume session \u2014 entry removed so the next hook event re-runs bootstrap via self-heal (#638 G1):",g);return}}if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:o,claudeSessionId:a,sessionState:h});return}this.subscribeToMobileEvents(o);try{await this.drainParkedE1Orphans(a,o,s)}catch(g){r.warn("E1: parked-orphan drain threw (non-fatal; TTL drain will retry)",{sessionId:o,error:g instanceof Error?g.message:String(g)})}if(!await this.appSyncClient.startHeartbeat(o)){r.error("Initial Claude session heartbeat was not persisted",{sessionId:o});let g=this.encryptedKeyRecovery;if(g?.claudeSessionId===a&&g.backendSessionId===o){let p=this.enterEncryptedKeyRecovery(e,o);this.appSyncClient.cleanupSubscription(o),await this.invalidateEncryptedRecoveryLiveness(p);return}}if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:o,authoritativeSessionId:o,claudeSessionId:a,sessionState:h});return}let f=this.activeSessions.get(o);if(f){f.mobileEndWatcher=this.appSyncClient.watchForMobileEnd(o,async()=>{r.info("Mobile ended session \u2014 sending desktop quit",{sessionId:o}),this.appSyncClient.stopHeartbeat(o),this.appSyncClient.cleanupSubscription(o);try{await this.appSyncClient.updateSession({sessionId:o,status:d.SessionStatus.INACTIVE}),r.info("Marked session INACTIVE on mobile-end (before tmux terminate)",{sessionId:o})}catch(S){r.warn("Failed to mark session INACTIVE on mobile-end",{sessionId:o,error:S})}let p=process.env[J];if(!p){r.warn("No tmux session set; skipping desktop self-terminate",{sessionId:o,expectedEnv:J});return}await Ze(p,ze)});let g=!1;try{f.orchestrationPromptWatcher=this.subscribeToOrchestrationPrompts(o)}catch(p){g=!0,r.warn("[v1-bridge] Failed to start orchestration prompt watcher",{sessionId:o,error:String(p)})}try{f.orchestrationDecisionWatcher=await this.subscribeToApplyUserDecision(f)}catch(p){g=!0,r.warn("[v1-bridge] Failed to start applyUserDecision watcher",{sessionId:o,error:String(p)})}g&&this.emitBridgeUnavailableBanner()}this.clearEncryptedKeyRecovery("session key recovered and liveness established",a,o)}recoverRetiredBackendSession(e,t){let i=this.retiredSessionRecoveryPromises.get(e);if(i)return i;if(this.isStopping)return Promise.resolve();let n=t.claudeSessionId,s={ownershipGeneration:this.claudeOwnershipGeneration,sessionIntentVersion:this.getClaudeSessionIntentVersion(n)},a=()=>this.isClaudeStartIntentCurrent(n,s),c=this.enqueueSessionLifecycle(async()=>{if(this.isStopping||!a()||this.activeSessions.get(e)!==t||this.claudeToBackendSessionId.get(t.claudeSessionId)!==e)return;let u={session_id:t.claudeSessionId,metadata:t.metadata,transcript_path:t.transcriptPath};r.warn("Backend retired live Claude session; creating replacement row",{sessionId:e,claudeSessionId:t.claudeSessionId}),this.parkE1Orphans(t.claudeSessionId,this.snapshotOpenE1(e));let m=++this.lifecycleGeneration;if(await this.handleSessionEndSerialized({session_id:t.claudeSessionId,metadata:{reason:"backend-generation-retired"}}),!this.isLifecycleCurrent(m)||!a())return;let h=`claude-${t.claudeSessionId}-${(0,j.randomUUID)()}`;await this.handleSessionStartSerialized(u,h,m,a)}).finally(()=>{this.retiredSessionRecoveryPromises.get(e)===c&&this.retiredSessionRecoveryPromises.delete(e)});return this.retiredSessionRecoveryPromises.set(e,c),c}isLifecycleCurrent(e){return!this.isStopping&&this.lifecycleGeneration===e}authoritativeSessionIdFromError(e){let t=e?.authoritativeSessionId;return typeof t=="string"&&t.length>0?t:null}async retireCancelledClaudeBootstrap(e){let t=new Set([e.requestedSessionId,e.authoritativeSessionId]);for(let i of t)this.appSyncClient.stopHeartbeat(i),this.appSyncClient.cleanupSubscription(i);this.activeSessions.get(e.requestedSessionId)===e.sessionState&&this.activeSessions.delete(e.requestedSessionId),this.activeSessions.get(e.authoritativeSessionId)===e.sessionState&&this.activeSessions.delete(e.authoritativeSessionId),this.claudeToBackendSessionId.get(e.claudeSessionId)===e.requestedSessionId&&this.claudeToBackendSessionId.delete(e.claudeSessionId),this.removePortFile(e.claudeSessionId);try{await this.appSyncClient.updateSession({sessionId:e.authoritativeSessionId,status:d.SessionStatus.INACTIVE}),r.info("Cancelled Claude bootstrap row marked INACTIVE",{sessionId:e.authoritativeSessionId,claudeSessionId:e.claudeSessionId})}catch(i){r.warn("Cancelled Claude bootstrap row could not be marked INACTIVE",{sessionId:e.authoritativeSessionId,error:i instanceof Error?i.message:String(i)})}}async emitBridgeUnavailableBanner(){let e=process.env[J],t="[CodeVibe] Cross-device decision sync unavailable \u2014 mobile responses will be sent as free-text";if(r.warn(t),!!e)try{await oe(`tmux display-message -t "${e}" -- ${JSON.stringify(t)}`)}catch(i){r.warn("[v1-bridge] tmux display-message for bridge-unavailable banner failed",{tmuxSession:e,error:String(i)})}}handleSessionEnd(e){return this.isStopping?Promise.resolve():(this.recordExplicitClaudeSessionEnd(e.session_id),this.enqueueSessionLifecycle(()=>this.handleSessionEndSerialized(e)))}async handleSessionEndSerialized(e){let t=e.session_id,i=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);r.info("Session ended",{claudeSessionId:t,sessionId:i,reason:e.metadata?.reason});let n=this.activeSessions.get(i);if(n?.mobileEndWatcher&&(n.mobileEndWatcher.stop(),n.mobileEndWatcher=void 0),n?.orchestrationPromptWatcher&&(n.orchestrationPromptWatcher.stop(),n.orchestrationPromptWatcher=void 0),n?.orchestrationDecisionWatcher&&(n.orchestrationDecisionWatcher.stop(),n.orchestrationDecisionWatcher=void 0),n?.orchestrationClient){try{await n.orchestrationClient.destroy()}catch(a){r.warn("[v1-bridge] OrchestrationClient.destroy() failed",{sessionId:i,error:String(a)})}n.orchestrationClient=void 0}this.toolActivity&&this.toolActivity.sessionId===i&&(await this.toolActivity.stop(),this.toolActivity=null),this.activeSessions.delete(i),this.claudeToBackendSessionId.delete(t),this.removePortFile(t),n?.waitingForPromptResponse&&(r.info("Clearing prompt wait state - session ending",{sessionId:i}),this.clearPromptState(n));let s=this.toolActivityLegacyRecorders.get(i);if(s&&s.disposeLegacyCoordination(),this.toolActivityLegacyRecorders.delete(i),this.appSyncClient.stopHeartbeat(i),this.appSyncClient.cleanupSubscription(i),n)try{await this.appSyncClient.updateSession({sessionId:i,status:d.SessionStatus.INACTIVE}),r.info("Session marked as INACTIVE in AppSync",{sessionId:i})}catch(a){r.warn("Failed to update session in AppSync:",a)}else r.warn("Cannot update session - session state not found",{sessionId:i});r.debug("Session cleanup completed",{sessionId:i})}subscribeToMobileEvents(e){r.info("Subscribing to mobile events",{sessionId:e});let t=this.activeSessions.get(e);if(!t){r.error("Session not found",{sessionId:e});return}this.appSyncClient.subscribeToEvents(e,async i=>{r.info("Received mobile event",{eventId:i.eventId,type:i.type,sessionId:i.sessionId,isEncrypted:i.isEncrypted});let n=t.pendingPanePromptIdentity,s=i.content||"";if(i.isEncrypted&&this.sessionKey)try{s=d.cryptoService.decryptContent(i.content,this.sessionKey),r.debug("Event decrypted successfully",{eventId:i.eventId})}catch(o){r.error("Failed to decrypt event:",{eventId:i.eventId,error:o}),s=i.content}let a={...i,content:s};try{await this.appSyncClient.updateEventStatus({eventId:i.eventId,sessionId:i.sessionId,timestamp:i.timestamp,deliveryStatus:d.DeliveryStatus.DELIVERED}),r.info("Event marked as DELIVERED",{eventId:i.eventId})}catch(o){r.warn("Failed to mark event as DELIVERED",{eventId:i.eventId,error:o})}if(i.type===d.EventType.USER_PROMPT){let o=this.activeSessions.get(e);if(o?.pendingOrchestrationGate&&await this.handleOrchestrationPromptResponse(o,s,i))return;if(o?.waitingForPromptResponse){let c=s.trim(),u=o.pendingSubmitMap?Object.keys(o.pendingSubmitMap).length:3,m=this.parseInteractivePromptInput(c,u);if(r.info("Parsed interactive prompt input",{sessionId:e,content:c,parsed:m,hasSubmitMap:!!o.pendingSubmitMap}),m.action==="select_option"){let h=o.pendingSubmitMap?.[m.option]||m.option;if(r.info("User selected option",{option:m.option,terminalInput:h}),await this.answerVerifiedInteractivePrompt(o,n,h)){await this.markEventExecuted(i),o.waitingForPromptResponse=!1,o.pendingPromptId=void 0,o.pendingSubmitMap=void 0,o.pendingPanePromptIdentity=void 0;let f=this.encryptForEmit(`Selected option ${m.option}`,{promptAnswered:!0},d.EventType.NOTIFICATION,e);f&&await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:f.content,metadata:f.metadata,isEncrypted:f.isEncrypted,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e})})}else await this.sendPromptError(e,"Failed to select option")}else if(m.action==="option_with_followup"){let h=o.pendingSubmitMap?.[m.option]||m.option;r.info("User selected option with follow-up",{option:m.option,terminalInput:h,followUpText:m.followUpText});let l=await this.answerVerifiedInteractivePrompt(o,n,h);if(o.waitingForPromptResponse=!1,o.pendingPromptId=void 0,o.pendingSubmitMap=void 0,o.pendingPanePromptIdentity=void 0,l){let f=this.encryptForEmit(`Selected option ${m.option}`,{promptAnswered:!0},d.EventType.NOTIFICATION,e);if(f&&await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:f.content,metadata:f.metadata,isEncrypted:f.isEncrypted,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e})}),m.followUpText){await new Promise(p=>setTimeout(p,1e3));let g={...i,content:m.followUpText};await this.executeMobilePrompt(e,g)}await this.markEventExecuted(i)}else await this.sendPromptError(e,"Failed to select option")}else{if(r.info("Rejecting active prompt before forwarding free-form mobile input",{responsePreview:c.slice(0,80),attachmentCount:a.attachments?.length??0}),!await this.sendVerifiedPromptKey(o,n,"Escape")){await this.sendPromptError(e,"Failed to dismiss the active prompt; your message was not sent");return}o.waitingForPromptResponse=!1,o.pendingPromptId=void 0,o.pendingSubmitMap=void 0,o.pendingPanePromptIdentity=void 0,await new Promise(l=>setTimeout(l,200)),await this.executeMobilePrompt(e,a)}}else await this.executeMobilePrompt(e,a)}},i=>{r.error("Subscription error",{sessionId:e,error:i})},{onSessionRetired:()=>this.recoverRetiredBackendSession(e,t)}),t.subscriptionActive=!0,r.info("Subscription active",{sessionId:e})}async dispatchMobileEvent(e,t){r.info("Received mobile event",{eventId:t.eventId,type:t.type,sessionId:t.sessionId,isEncrypted:t.isEncrypted});let i,n,s=!1,a,o,c;if(t.type===d.EventType.USER_PROMPT||t.type===d.EventType.PROMPT_RESPONSE)if(n=this.activeSessions.get(e),!n)i="no-session";else if(n.processedEventIds?.has(t.eventId))i="skip-dedup";else if(n.inFlightEventIds?.has(t.eventId))i="drop-event-redeliver";else if(n.waitingForPromptResponse){let l=n.promptGenerationToken;if(!l)i="regular";else if(t.type===d.EventType.USER_PROMPT&&n.hasReceivedPromptResponse&&(!t.promptId||t.promptId.length===0))i="drop-stale-answer";else if(t.promptId&&t.promptId.length>0&&l.promptId.length>0&&t.promptId!==l.promptId)i="drop-stale-answer";else{let f=l.promptId.length>0?l.promptId:`__prompt_gen_${l.gen}`;n.inFlightPromptIds?.has(f)?i="drop-in-flight":(n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightEventIds||(n.inFlightEventIds=new Set),n.inFlightPromptIds.add(f),n.inFlightEventIds.add(t.eventId),s=!0,o=f,c=t.eventId,a={promptId:l.promptId,gen:l.gen},i="walker")}}else i="regular";else i="not-user-prompt";let m=t.content||"";if(t.isEncrypted&&this.sessionKey)try{m=d.cryptoService.decryptContent(t.content,this.sessionKey),r.debug("Event decrypted successfully",{eventId:t.eventId})}catch(l){r.error("Failed to decrypt event:",{eventId:t.eventId,error:l}),m=t.content}let h={...t,content:m};if(i!=="skip-dedup")try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:d.DeliveryStatus.DELIVERED}),r.info("Event marked as DELIVERED",{eventId:t.eventId})}catch(l){r.warn("Failed to mark event as DELIVERED",{eventId:t.eventId,error:l})}if(i==="skip-dedup"){r.info("[walker] Subscription-level dedup hit (already processed) \u2014 skipping",{sessionId:e,eventId:t.eventId});return}if(i==="drop-stale-answer"){r.info("[walker] Stale answer dropped \u2014 event.promptId does not match current pending promptId",{sessionId:e,eventId:t.eventId,eventPromptId:t.promptId,currentPromptId:n?.promptGenerationToken?.promptId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("[walker] markEventExecuted threw on stale-answer drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}if(i==="drop-in-flight"){r.warn("[walker] Subscription-level in-flight guard \u2014 dropping duplicate USER_PROMPT (different eventId, same prompt)",{sessionId:e,eventId:t.eventId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("[walker] markEventExecuted threw on subscription-level duplicate drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}if(i==="drop-event-redeliver"){r.info("[walker] Subscription-level event-level redelivery \u2014 silent skip (original still in flight)",{sessionId:e,eventId:t.eventId});return}if(i==="walker"){t.type===d.EventType.PROMPT_RESPONSE&&n&&(n.hasReceivedPromptResponse=!0),await this.handleMobilePromptResponse(e,t,m,n,s,a,o,c);return}if(i==="regular"){if(t.type===d.EventType.PROMPT_RESPONSE){r.warn("Received PROMPT_RESPONSE with no active walker \u2014 dropping",{sessionId:e,eventId:t.eventId,promptId:t.promptId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("markEventExecuted threw on PROMPT_RESPONSE orphan drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}await this.executeMobilePrompt(e,h);return}if(i==="no-session"){r.warn("Received mobile prompt input for unknown session \u2014 ignoring",{sessionId:e,eventId:t.eventId,type:t.type});return}}async handleMobilePromptResponse(e,t,i,n,s=!1,a,o,c){let u=a??n.promptGenerationToken,m=o,h=c;if(!s&&u){let l=u.promptId.length>0?u.promptId:`__prompt_gen_${u.gen}`;if(n.inFlightPromptIds?.has(l)){r.warn("[walker] Duplicate mobile USER_PROMPT for same prompt \u2014 dropping",{sessionId:e,eventId:t.eventId,lockKey:l}),await this.markEventExecutedIdempotent(n,t);return}n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightEventIds||(n.inFlightEventIds=new Set),n.inFlightPromptIds.add(l),n.inFlightEventIds.add(t.eventId),m=l,h=t.eventId}try{if(!s&&n.processedEventIds?.has(t.eventId)){r.info("[walker] Redelivered event already processed \u2014 skipping",{sessionId:e,eventId:t.eventId});return}let l=i.trim(),f=n.pendingPromptId,g=n.pendingSubmitMap,p=n.pendingPanePromptIdentity,S=g,N=S?Object.keys(S).length:3,w=this.parseInteractivePromptInput(l,N);r.info("Parsed interactive prompt input",{sessionId:e,content:l,parsed:w,hasSubmitMap:!!S});let v=()=>{let C=n.promptGenerationToken,b=C?.gen,O=u?.gen;return b!==O?(r.warn("[walker] Token mismatch \u2014 external cleanup or new prompt during in-flight handler \u2014 aborting",{sessionId:e,eventId:t.eventId,entryToken:u,currentToken:C}),!0):!1};if(v()){await this.markEventExecutedIdempotent(n,t);return}{let C=n.pendingQuestionsQueue!==void 0,b=l.trim(),O=b.match(/^(\d+)$/);if(C&&n.pendingCurrentQuestion&&O){let I=n.pendingCurrentQuestion.options?.length??0,R=O[1],k=parseInt(R,10),_=!Number.isFinite(k)||k<1||k>I,E=String(k)!==R;if(_||E){let F=this.getWalkerPosition(n);if(r.info("AUQ walker \u2014 bare out-of-range or non-canonical option; routing per dispatch matrix",{sessionId:e,option:R,optionNum:k,realOptionCount:I,isOutOfRange:_,isNonCanonical:E,walkerPosition:F,parsedAction:w.action}),await this.markEventExecutedIdempotent(n,t),v())return;if(F==="on_synth"){let x=await this.answerVerifiedInteractivePrompt(n,p,"2",{pressEnter:!1});if(v())return;if(!x){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(A){r.warn("emitUserChoice on on-SYNTH Cancel failed",{sessionId:e,error:A instanceof Error?A.message:String(A)})}return}if(await new Promise(A=>setTimeout(A,1500)),v())return;let M=await this.sendFreeFormWhenNoChooser(e,n,b);if(v())return;M||r.warn("on-SYNTH Cancel followup text-send failed; AUQ cancelled, no new prompt",{sessionId:e});try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(A){r.warn("emitUserChoice on on-SYNTH Cancel success failed",{sessionId:e,error:A instanceof Error?A.message:String(A)})}if(v())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,p,b,v);return}}}if(w.action==="select_option"){let C=S?.[w.option]||w.option,b=n.pendingQuestionsQueue!==void 0;r.info("User selected option",{option:w.option,terminalInput:C,isV2AskUserQuestion:b});let O=await this.answerVerifiedInteractivePrompt(n,p,C,{pressEnter:!b});if(v()){await this.markEventExecutedIdempotent(n,t);return}if(O){if(await this.markEventExecutedIdempotent(n,t),v())return;if(!f){r.warn("emitAnswerAck called without promptId \u2014 clearing state + skipping ack",{sessionId:e,source:"select_option",eventId:t.eventId}),this.promoteFingerprintAndClearPromptState(n);return}let I=(n.pendingQuestionsQueue?.length??0)===0;try{if(b){let V=parseInt(w.option,10)-1,q=n.pendingCurrentQuestion?.options?.[V],H=typeof q=="string"?q:q&&typeof q=="object"?q.label:`option ${w.option}`,he=n.pendingCurrentQuestion?._isSubmit===!0,ge=H.toLowerCase(),ee;he&&ge==="cancel"?ee="\u2192 Cancel \u2014 AskUserQuestion cancelled, no answers submitted":he&&ge.startsWith("submit")?ee="\u2192 Submit answers \u2014 AskUserQuestion completed":ee=`\u2192 ${H}`,await this.emitUserChoice(e,ee)}else await this.emitAnswerAck(e,`Selected option ${w.option}`,{promptId:f,questionIndex:0,isTerminal:I})}catch(V){r.warn("[walker] user-choice/ack emit failed \u2014 continuing to STEP 7/8",{sessionId:e,promptId:f,isV2AskUserQuestion:b,error:V instanceof Error?V.message:String(V)})}if(v())return;let R=n.pendingQuestionsQueue?.shift();if(R&&(n.pendingCurrentQuestion=R,n.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(R)),!R){n.activeAskUserQuestionFingerprint&&(n.completedAskUserQuestionFingerprints||(n.completedAskUserQuestionFingerprints=new Set),n.completedAskUserQuestionFingerprints.add(n.activeAskUserQuestionFingerprint),r.info("AskUserQuestion V2 walker complete \u2014 fingerprint marked closed",{sessionId:e,fingerprint:n.activeAskUserQuestionFingerprint.slice(0,16)})),this.clearPromptState(n);return}let k=this.newE1Raise(e,!0),_=k.promptId;if(!_){this.promoteFingerprintAndClearPromptState(n),r.warn("Q[next] emit aborted: synthesized promptId was empty; promoted fingerprint + cleared prompt state",{sessionId:e,eventId:t.eventId});return}let E=n.pendingSynthTail??[],F=this.buildQuestionWireData(R,E),x=R.question,M={tool_name:"AskUserQuestion",tool_input:{questions:[R]},options:F.options,submitMap:F.submitMap,instructions:F.instructions},A=n.encryptionSnapshot,L=(0,d.encryptForEmit)(A?.sessionKey??null,A?.sessionIsEncrypted??!1,x,M,A?A.encryptionResolved:!1);if(!L){r.error("No session key for ENCRYPTED session \u2014 dropping Q[next] prompt (fail-closed, #638)",{sessionId:e,promptId:_}),this.promoteFingerprintAndClearPromptState(n);return}let z=L.content,Y=L.metadata,B=L.isEncrypted,U=this.nextPromptGen++,P={promptId:_,gen:U};n.pendingPromptId=_,n.pendingSubmitMap=F.submitMap,n.promptGenerationToken=P;let $=P,Z=this.activeSessions.get(e)?.promptGenerationToken;if(!Z||Z.gen!==$.gen||Z.promptId!==$.promptId){r.warn("Q[next] emit aborted: token replaced before await dispatch",{sessionId:e,tokenAtAwait:$,currentToken:Z});return}let me=_.length>0?_:`__prompt_gen_${P.gen}`;n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightPromptIds.add(me);try{this.stashE1RaiseContent(_,x,M);try{await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.INTERACTIVE_PROMPT,source:d.EventSource.DESKTOP,content:z,metadata:Y,...(0,d.raiseFieldsFromRecord)(k),timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:B?!0:void 0}),r.info("Q[next] emit succeeded",{sessionId:e,promptId:_,remaining:n.pendingQuestionsQueue?.length??0})}catch(V){let q=this.activeSessions.get(e),H=q?.promptGenerationToken;H&&H.gen===$.gen&&H.promptId===$.promptId?(this.promoteFingerprintAndClearPromptState(q),r.warn("Q[next] emit failed; promoted fingerprint + cleared prompt state. User must answer remaining questions on desktop terminal.",{sessionId:e,promptId:_,error:V instanceof Error?V.message:String(V)})):r.warn("Q[next] emit failed but a NEW prompt replaced our token during await; not clearing state (would wipe new prompt). Q[next..QN] of the original AskUserQuestion are lost; new prompt continues normally.",{sessionId:e,tokenAtAwait:$,currentToken:H,error:V instanceof Error?V.message:String(V)})}}finally{n.inFlightPromptIds.delete(me)}}else try{await this.sendPromptError(e,"Failed to select option")}catch(I){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(I)})}finally{await this.markEventExecutedIdempotent(n,t)}}else if(w.action==="option_with_followup"){if(n.pendingQuestionsQueue!==void 0){let I=this.getWalkerPosition(n),R=n.pendingCurrentQuestion?.options?.length??0,k=w.option?parseInt(w.option,10):NaN,_=Number.isFinite(k)&&k>R,E=w.followUpText??"";if(r.info("AUQ walker \u2014 option_with_followup dispatch",{sessionId:e,option:w.option,optionNum:k,explicitCount:R,isSynthDigit:_,walkerPosition:I,followUpTextLen:E.length}),await this.markEventExecutedIdempotent(n,t),v())return;if(_){if(I==="on_synth"){let U=await this.answerVerifiedInteractivePrompt(n,p,"2",{pressEnter:!1});if(v())return;if(!U){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(P){r.warn("emitUserChoice on synth-digit on-SYNTH Cancel failed",{sessionId:e,error:P instanceof Error?P.message:String(P)})}return}if(await new Promise(P=>setTimeout(P,1500)),v())return;if(E){let P=await this.sendFreeFormWhenNoChooser(e,n,E);if(v())return;P||r.warn("on-SYNTH Cancel followup text-send failed",{sessionId:e})}try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(P){r.warn("emitUserChoice on synth-digit on-SYNTH success failed",{sessionId:e,error:P instanceof Error?P.message:String(P)})}if(v())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,p,E,v);return}let F=S?.[w.option]||w.option,x=k-1,M=n.pendingCurrentQuestion?.options?.[x],A=typeof M=="string"?M:M&&typeof M=="object"?M.label:`option ${w.option}`;if(I==="q1"){let U=await this.answerVerifiedInteractivePrompt(n,p,F,{pressEnter:!1});if(v())return;if(!U){try{await this.sendPromptError(e,"Failed to select option. Your reply (including the follow-up text) was not sent. Please retry.")}catch(P){r.warn("sendPromptError on q1 option_with_followup failed",{sessionId:e,error:P instanceof Error?P.message:String(P)})}return}if(await new Promise(P=>setTimeout(P,1500)),v())return;if(E){let P=await this.sendFreeFormWhenNoChooser(e,n,E);if(v())return;P||r.warn("q1 option_with_followup followup text-send failed",{sessionId:e})}try{await this.emitUserChoice(e,E?`\u2192 ${A} + sending your reply as a new prompt`:`\u2192 ${A}`)}catch(P){r.warn("emitUserChoice on q1 option_with_followup success failed",{sessionId:e,error:P instanceof Error?P.message:String(P)})}if(v())return;this.promoteFingerprintAndClearPromptState(n);return}if(I==="nq_mid"){let U=await this.answerVerifiedInteractivePrompt(n,p,F,{pressEnter:!1});if(v())return;if(!U){try{await this.sendPromptError(e,"Failed to commit Q[i]. Your reply was not sent. Please retry.")}catch($){r.warn("sendPromptError on nq_mid option_with_followup failed",{sessionId:e,error:$ instanceof Error?$.message:String($)})}return}if(await new Promise($=>setTimeout($,200)),v())return;let P=n.pendingQuestionsQueue?.shift();P?(n.pendingCurrentQuestion=P,n.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(P)):r.warn("nq_mid option_with_followup: queue.shift returned undefined unexpectedly",{sessionId:e}),await this.handleMobileReplyAsDismissAndPrompt(e,n,n.pendingPanePromptIdentity,E,v);return}if(I==="nq_last"){await this.handleOptionWithFollowupOnLastQ(e,n,p,F,A,E,v);return}let L=await this.answerVerifiedInteractivePrompt(n,p,F,{pressEnter:!1});if(v())return;if(!L){try{await this.sendPromptError(e,"Failed to send SYNTH keypress. Your reply was not sent. Please retry.")}catch(U){r.warn("sendPromptError on on-SYNTH option_with_followup failed",{sessionId:e,error:U instanceof Error?U.message:String(U)})}return}if(await new Promise(U=>setTimeout(U,1500)),v())return;if(E){let U=await this.sendFreeFormWhenNoChooser(e,n,E);if(v())return;U||r.warn("on-SYNTH option_with_followup text-send failed",{sessionId:e})}let z=n.pendingCurrentQuestion?._isSubmit===!0,Y=A.toLowerCase(),B;z&&Y==="cancel"?B=E?"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt":"\u2192 Cancel \u2014 AskUserQuestion cancelled, no answers submitted":z&&Y.startsWith("submit")?B=E?"\u2192 Submit answers \u2014 AskUserQuestion completed, sending your reply as a new prompt":"\u2192 Submit answers \u2014 AskUserQuestion completed":B=E?`\u2192 ${A} + sending your reply as a new prompt`:`\u2192 ${A}`;try{await this.emitUserChoice(e,B)}catch(U){r.warn("emitUserChoice on on-SYNTH option_with_followup success failed",{sessionId:e,error:U instanceof Error?U.message:String(U)})}if(v())return;this.promoteFingerprintAndClearPromptState(n);return}let b=S?.[w.option]||w.option;r.info("User selected option with follow-up",{option:w.option,terminalInput:b,followUpText:w.followUpText});let O=await this.answerVerifiedInteractivePrompt(n,p,b);if(v()){await this.markEventExecutedIdempotent(n,t);return}if(O){if(await this.markEventExecutedIdempotent(n,t),v())return;if(!f){r.warn("emitAnswerAck called without promptId \u2014 clearing state + skipping ack",{sessionId:e,source:"option_with_followup",eventId:t.eventId}),this.promoteFingerprintAndClearPromptState(n);return}try{await this.emitAnswerAck(e,`Selected option ${w.option}`,{promptId:f,questionIndex:0,isTerminal:!0})}catch(I){r.warn("[walker] emitAnswerAck (option_with_followup) failed \u2014 continuing to clearPromptState + executeMobilePrompt",{sessionId:e,promptId:f,error:I instanceof Error?I.message:String(I)})}if(v())return;if(this.promoteFingerprintAndClearPromptState(n),w.followUpText){await new Promise(R=>setTimeout(R,1e3));let I={...t,content:w.followUpText};await this.executeMobilePrompt(e,I)}}else try{await this.sendPromptError(e,"Failed to select option. Your reply (including the follow-up text) was not sent. Please retry.")}catch(I){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(I)})}finally{await this.markEventExecutedIdempotent(n,t)}}else{if(n.pendingQuestionsQueue!==void 0){let O=this.getWalkerPosition(n);if(r.info("AUQ walker \u2014 send_as_response dispatch",{sessionId:e,walkerPosition:O,contentPreview:l.slice(0,80)}),await this.markEventExecutedIdempotent(n,t),v())return;if(O==="on_synth"){let I=await this.answerVerifiedInteractivePrompt(n,p,"2",{pressEnter:!1});if(v())return;if(!I){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(k){r.warn("emitUserChoice on send_as_response on-SYNTH Cancel failed",{sessionId:e,error:k instanceof Error?k.message:String(k)})}return}if(await new Promise(k=>setTimeout(k,1500)),v())return;let R=await this.sendFreeFormWhenNoChooser(e,n,l);if(v())return;R||r.warn("send_as_response on-SYNTH text-send failed",{sessionId:e});try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(k){r.warn("emitUserChoice on send_as_response on-SYNTH success failed",{sessionId:e,error:k instanceof Error?k.message:String(k)})}if(v())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,p,l,v);return}r.info("Rejecting legacy prompt before forwarding free-form mobile input",{responsePreview:l.slice(0,80),attachmentCount:t.attachments?.length??0});let b=await this.sendVerifiedPromptKey(n,p,"Escape");if(v()){await this.markEventExecutedIdempotent(n,t);return}if(b)this.promoteFingerprintAndClearPromptState(n),await new Promise(O=>setTimeout(O,200)),await this.executeMobilePrompt(e,{...t,content:l});else try{await this.sendPromptError(e,"Failed to dismiss the active prompt; your message was not sent")}catch(O){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(O)})}finally{await this.markEventExecutedIdempotent(n,t)}}}finally{m&&n.inFlightPromptIds&&n.inFlightPromptIds.delete(m),h&&n.inFlightEventIds&&n.inFlightEventIds.delete(h)}}async sendInteractivePromptAsync(e,t,i){let n=this.activeSessions.get(e),s=n?.promptGenerationToken?{...n.promptGenerationToken}:void 0,a=(0,d.prepareEventTimestamp)({orderingKey:e});await new Promise(I=>setTimeout(I,500));let o=process.env.CODEVIBE_TMUX_SESSION,c={...t.metadata||{}},u,m=t.metadata?.tool_name,h=t.metadata?.tool_input,l=m==="AskUserQuestion"&&Array.isArray(h?.questions)?h.questions:[];if(l.length>0&&Array.isArray(l[0]?.options)&&l[0].options.length>0){let I=l[0];u=this.buildAskUserQuestionPaneIdentity(I);let R=[];if(o)try{let{exec:x}=await import("child_process"),M=L=>new Promise((z,Y)=>{x(L,{timeout:5e3},(B,U)=>{B?Y(B):z({stdout:U||""})})}),{stdout:A}=await M(`tmux capture-pane -p -e -S -30 -t '${o}'`);R=this.parseAskUserQuestionSynthTail(A),r.info("AskUserQuestion synth-tail parsed from tmux",{tailCount:R.length,tail:R.map(L=>L.label)})}catch(x){r.warn("Failed to capture tmux for AskUserQuestion synth-tail; emitting without synth tail",{error:x instanceof Error?x.message:String(x)})}else r.info("No tmux session \u2014 AskUserQuestion synth tail will be empty");let k=this.activeSessions.get(e);if(k){let x=k.promptGenerationToken;s&&x?.gen===s.gen?k.pendingSynthTail=R:r.warn("AskUserQuestion synth-tail: stale async \u2014 token gen mismatch, skipping pendingSynthTail write",{tokenAtEmit:s,currentToken:x,sessionId:e})}let _=this.buildQuestionWireData(I,R);c.options=JSON.parse(JSON.stringify(_.options)),c.submitMap=JSON.parse(JSON.stringify(_.submitMap)),c.instructions=_.instructions,c.tool_name="AskUserQuestion",c.tool_input={questions:[I]},i=I.question;let E=typeof t.prompt_id=="string"&&t.prompt_id.length>0,F=tt(l);if(E){let x=this.activeSessions.get(e);if(x){let M=x.promptGenerationToken;s&&M?.gen===s.gen?(x.pendingQuestionsQueue=F.remainingQueue,x.pendingCurrentQuestion=I,x.pendingPanePromptIdentity=u,x.hasReviewScreen=F.hasReviewScreen):r.warn("AskUserQuestion: stale async \u2014 token gen mismatch, skipping walker-state write",{tokenAtEmit:s,currentToken:M,sessionId:e})}}else r.warn("AskUserQuestion: empty prompt_id, degrading to single-Q legacy emit",{questionCount:l.length});r.info("AskUserQuestion: emitting Q1 only (Q2..QN queued)",{questionCount:F.questionCount,hasReviewScreen:F.hasReviewScreen,queuedRemaining:E?F.remainingQueue.length:0,optionCountFirst:_.options.length,questionPreview:I.question.slice(0,80)})}else if(o)try{let{exec:I}=await import("child_process"),R=F=>new Promise((x,M)=>{I(F,{timeout:5e3},(A,L)=>{A?M(A):x({stdout:L||""})})}),{stdout:k}=await R(`tmux capture-pane -p -e -S -30 -t '${o}'`),_=k.split(`
4
- `);r.info("tmux capture result",{tmuxSession:o,totalLines:_.length,lastLines:_.slice(-15).map(F=>F.replace(/\x1B[^m]*m/g,"").trim()).filter(Boolean)});let E=(0,d.parseInteractivePrompt)(k);if(E&&E.options.length>0)c.options=E.options,c.submitMap=E.submitMap,c.instructions=this.buildPromptInstructions(E),u=this.buildParsedPanePromptIdentity(E),r.info("Parsed dynamic options from tmux",{optionCount:E.options.length,kind:E.kind,options:E.options});else{this.suppressPromptOptionsUnavailable(e,s,"tmux parse returned no options");return}}catch(I){this.suppressPromptOptionsUnavailable(e,s,`tmux capture failed: ${I instanceof Error?I.message:String(I)}`);return}else{this.suppressPromptOptionsUnavailable(e,s,"no tmux session");return}let f=this.activeSessions.get(e),g=!s||f?.promptGenerationToken?.gen===s.gen;f&&c.submitMap&&g&&(f.pendingSubmitMap=c.submitMap,f.pendingPanePromptIdentity=u);let p=this.encryptForEmit(i,c,d.EventType.INTERACTIVE_PROMPT,e);if(!p)return;let S=p.content,N=p.metadata,w=p.isEncrypted,C=this.activeSessions.get(e)?.promptGenerationToken;if(s&&C?.gen!==s.gen){r.warn("Interactive prompt emit: stale token \u2014 newer INTERACTIVE_PROMPT replaced ours; skipping AppSync emit",{sessionId:e,tokenAtEmit:s,currentToken:C});return}let b=t.prompt_id?this.e1PromptRaises.get(t.prompt_id):void 0,O=b?(0,d.raiseFieldsFromRecord)(b.record):{};b&&this.stashE1RaiseContent(b.record.promptId,i,c),await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.INTERACTIVE_PROMPT,source:t.source,content:S,metadata:N,promptId:t.prompt_id,...O,timestamp:a,isEncrypted:w?!0:void 0}),r.info("Interactive prompt sent to AppSync with dynamic options",{sessionId:e})}buildQuestionWireData(e,t=[]){let i=(e.options||[]).map((c,u)=>{let m=typeof c=="string",h=m?c:c.label||"",l=m?"":c.description||"",f=m?"":c.preview||"",g={number:String(u+1),text:h};return l&&(g.description=l),f&&(g.preview=f),g});if(!e._isSubmit)for(let c of t)i.push({number:String(i.length+1),text:c.label});let n=Object.fromEntries(i.map(c=>[c.number,c.number])),s=(e.options||[]).length,a=t.findIndex(c=>c.label==="Type something"),o;if(e._isSubmit)o="Reply with 1 to submit your answers or 2 to cancel.";else if(e.multiSelect)o=`Reply with comma-separated numbers (e.g., 1,3) for "${e.header||e.question}"`;else if(a>=0){let c=String(s+a+1);o=`Reply with the number of your choice. For option ${c} (Type something), reply "${c}, your answer".`}else o="Reply with the number of your choice.";return{options:i,submitMap:n,instructions:o}}parseAskUserQuestionSynthTail(e){let t=(0,d.normalizeSnapshot)(e);if(!t)return[];let i=t.split(`
5
- `).slice(-14),n=[],s=new Set,a=/^\s*(?:[›❯▸▶➜➤]\s*)?(?:\d+\.\s+)?(Type something|Chat about this)\.?\s*$/i;for(let o of i){let c=o.match(a);if(!c)continue;let m=c[1].toLowerCase()==="type something"?"Type something":"Chat about this";s.has(m)||(n.push({label:m}),s.add(m))}return n}suppressPromptOptionsUnavailable(e,t,i){r.warn("Interactive prompt: real options unavailable \u2014 suppressing mobile prompt (no fabricated options)",{sessionId:e,reason:i});let n=t?.promptId,s=n?this.e1PromptRaises.get(n):void 0;s&&(s.record.mobileActionable=!1,this.emitSuppressedPromptCarrier(e,s.record).catch(o=>{r.error("E1: failed to emit suppressed-prompt badge carrier",{sessionId:e,promptId:s.record.promptId,error:o instanceof Error?o.message:String(o)})}));let a=this.activeSessions.get(e);a&&t&&a.promptGenerationToken?.gen===t.gen&&this.clearPromptState(a)}async emitSuppressedPromptCarrier(e,t){let i="\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.",n=this.encryptForEmit(i,{e1SuppressedPrompt:!0},d.EventType.NOTIFICATION,e);return n?(await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:n.content,metadata:n.metadata,...(0,d.raiseFieldsFromRecord)(t),notificationText:i,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:n.isEncrypted?!0:void 0}),r.info("E1: emitted suppressed-prompt badge carrier (mobileActionable=false)",{sessionId:e,promptId:t.promptId}),!0):!1}buildPromptInstructions(e){return`Reply with ${e.options.map(i=>i.number).join(", ")}. Append a message to provide alternative instructions.`}parseInteractivePromptInput(e,t=3){return Me(e,t)}async markEventExecuted(e){try{await this.appSyncClient.updateEventStatus({eventId:e.eventId,sessionId:e.sessionId,timestamp:e.timestamp,deliveryStatus:d.DeliveryStatus.EXECUTED}),r.info("Event marked as EXECUTED",{eventId:e.eventId})}catch(t){r.warn("Failed to mark event as EXECUTED",{eventId:e.eventId,error:t})}}async sendPromptError(e,t){let i={error:!0},n=this.encryptForEmit(t,i,d.EventType.NOTIFICATION,e);if(!n)return;let s=n.content,a=n.metadata,o=n.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:s,metadata:a,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:o?!0:void 0})}async emitUserChoice(e,t){let i=this.encryptForEmit(t,{source:"codevibe_v2_user_choice"},d.EventType.ASSISTANT_RESPONSE,e);if(!i)return;let n=i.content,s=i.metadata,a=i.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.ASSISTANT_RESPONSE,source:d.EventSource.DESKTOP,content:n,metadata:s,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:a?!0:void 0})}async emitAnswerAck(e,t,i){let n={promptAnswered:!0,...i},s=this.encryptForEmit(t,n,d.EventType.NOTIFICATION,e);if(!s)return;let a=s.content,o=s.metadata,c=s.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:a,metadata:o,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:c?!0:void 0})}promoteFingerprintAndClearPromptState(e){e.activeAskUserQuestionFingerprint&&(e.completedAskUserQuestionFingerprints||(e.completedAskUserQuestionFingerprints=new Set),e.completedAskUserQuestionFingerprints.add(e.activeAskUserQuestionFingerprint)),this.clearPromptState(e)}normalizePromptIdentityText(e){return(0,d.normalizeSnapshot)(e).replace(/\s+/g," ").trim().toLowerCase()}buildParsedPanePromptIdentity(e){let t=[e.kind,this.normalizePromptIdentityText(e.promptText),...e.options.map(i=>`${i.number}:${this.normalizePromptIdentityText(i.text)}`)].join("::");return{kind:"parsed",key:(0,j.createHash)("sha256").update(t).digest("hex")}}buildAskUserQuestionPaneIdentity(e){let t=this.normalizePromptIdentityText(e.question??""),i=(e.options??[]).map(s=>this.normalizePromptIdentityText(typeof s=="string"?s:[s.label,s.description,s.preview].filter(Boolean).join(" "))),n=["ask_user_question",t,...i].join("::");return{kind:"ask_user_question",key:(0,j.createHash)("sha256").update(n).digest("hex"),questionText:t,optionLabels:i}}async captureLivePromptPane(){let e=process.env.CODEVIBE_TMUX_SESSION;if(!e)throw new Error("CODEVIBE_TMUX_SESSION is unset");let t=e.replace(/'/g,"'\\''"),{stdout:i}=await oe(`tmux capture-pane -p -e -S -40 -t '${t}'`,{timeout:3e3});return i||""}liveAskUserQuestionMatches(e,t){let i=(0,d.normalizeSnapshot)(e).split(`
6
- `).map(p=>p.trim()).filter(Boolean);if(i.length===0||!t.questionText||!t.optionLabels)return!1;let n=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|Chat about this|Type something|Notes: press|\(esc\)|\(shift\+tab\))/i,s=i.length-1,a=-1;for(let p=s;p>=Math.max(0,s-3);p-=1)if(n.test(i[p])){a=p;break}if(a<0)return!1;let o=/^(?:[›❯▸▶➜➤]\s*)?(\d+)\.\s+(.+)$/,c=[];for(let p=Math.max(0,a-30);p<a;p+=1){let S=i[p].match(o);S&&c.push({index:p,number:Number(S[1]),firstLine:S[2]})}let u=[...c].reverse().find(p=>p.number===1);if(!u)return!1;let m=c.filter(p=>p.index>=u.index);if(m.length!==t.optionLabels.length||m.some((p,S)=>p.number!==S+1)||m.map((p,S)=>{let N=m[S+1]?.index??a,w=[p.firstLine,...i.slice(p.index+1,N).filter(v=>!n.test(v)&&!/^[─━-]+$/.test(v))];return this.normalizePromptIdentityText(w.join(" "))}).some((p,S)=>p!==t.optionLabels[S]))return!1;let l=m[0].index,f=Math.max(0,l-14);for(let p=l-1;p>=f;p-=1)if(n.test(i[p])){f=p+1;break}let g=this.normalizePromptIdentityText(i.slice(f,l).join(" "));return g===t.questionText||g.endsWith(` ${t.questionText}`)}livePromptHasNegativeAffordance(e){return(0,d.normalizeSnapshot)(e).split(`
7
- `).map(i=>i.trim()).filter(Boolean).slice(-3).some(i=>/(?:Esc to cancel|\(esc\))/i.test(i))}liveParsedPromptIsActive(e,t){let i=(0,d.normalizeSnapshot)(e).split(`
8
- `).map(c=>c.trim()).filter(Boolean);if(i.length===0)return!1;let n=i.length-1;if(t.kind==="yes_no"){let c=-1;for(let u=n;u>=Math.max(0,n-2);u-=1)if(/\[(?:y\/n|Y\/n|y\/N)\]/.test(i[u])){c=u;break}return c>=0}let s=/^(?:[›❯▸▶➜➤]\s*)?\d+\.\s+.+$/,a=-1;for(let c=n;c>=Math.max(0,n-6);c-=1)if(s.test(i[c])){a=c;break}if(a<0)return!1;let o=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|\(esc\)|\(shift\+tab\))/i;for(let c=n;c>=Math.max(a,n-1);c-=1)if(o.test(i[c]))return!0;return!1}async verifyLivePromptIdentity(e,t,i={}){if(!t||e.pendingPanePromptIdentity?.key!==t.key)return!1;try{let n=await this.captureLivePromptPane();if(e.pendingPanePromptIdentity?.key!==t.key||i.requireNegativeAffordance&&!this.livePromptHasNegativeAffordance(n))return!1;if(t.kind==="parsed"){let s=(0,d.parseInteractivePrompt)(n);return!!s&&this.liveParsedPromptIsActive(n,s)&&this.buildParsedPanePromptIdentity(s).key===t.key}return this.liveAskUserQuestionMatches(n,t)}catch(n){return r.warn("Unable to verify live Claude prompt before terminal input",{sessionId:e.sessionId,promptId:e.pendingPromptId,error:n instanceof Error?n.message:String(n)}),!1}}async answerVerifiedInteractivePrompt(e,t,i,n){if(!await this.verifyLivePromptIdentity(e,t))return r.warn("Refusing mobile answer because the live Claude prompt changed or is uninspectable",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1;let s=n?.pressEnter??!0,a=await this.promptResponder.answerInteractivePrompt(e.sessionId,i,{pressEnter:!1});return!a||!s?a:(await new Promise(o=>setTimeout(o,500)),await this.verifyLivePromptIdentity(e,t)?this.promptResponder.sendKey(e.sessionId,"Enter"):(r.warn("Refusing Enter because the Claude prompt changed after text input",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1))}async sendVerifiedPromptKey(e,t,i){return await this.verifyLivePromptIdentity(e,t,{requireNegativeAffordance:i==="Escape"})?this.promptResponder.sendKey(e.sessionId,i):(r.warn("Refusing mobile key because the live Claude prompt changed or is uninspectable",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1)}liveAnyPromptIsActive(e){let t=(0,d.parseInteractivePrompt)(e);if(t&&this.liveParsedPromptIsActive(e,t))return!0;let i=(0,d.normalizeSnapshot)(e).split(`
9
- `).map(s=>s.trim()).filter(Boolean),n=-1;for(let s=i.length-1;s>=0;s-=1)if(/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|\(esc\)|\(shift\+tab\))/i.test(i[s])){n=s;break}return n<Math.max(0,i.length-3)?!1:i.slice(Math.max(0,n-24),n).some(s=>/^(?:[›❯▸▶➜➤]\s*)?\d+\.\s+.+$/.test(s))}async sendFreeFormWhenNoChooser(e,t,i){let n=()=>this.activeSessions.get(e)===t,s=async()=>{try{return!this.liveAnyPromptIsActive(await this.captureLivePromptPane())}catch(o){return r.warn("Unable to prove the Claude chooser is absent before free-form input",{sessionId:e,error:o instanceof Error?o.message:String(o)}),!1}};return!n()||!await s()||!n()||!await this.promptResponder.answerInteractivePrompt(e,i,{pressEnter:!1})||(await new Promise(o=>setTimeout(o,500)),!n()||!await s()||!n())?!1:this.promptResponder.sendKey(e,"Enter")}clearPromptState(e){e.waitingForPromptResponse=!1,e.pendingPromptId=void 0,e.pendingSubmitMap=void 0,e.pendingPanePromptIdentity=void 0,e.pendingQuestionsQueue=void 0,e.pendingCurrentQuestion=void 0,e.pendingSynthTail=void 0,e.hasReviewScreen=void 0,e.activeAskUserQuestionFingerprint=void 0,e.promptGenerationToken=void 0}async detectWalkerActive(e){let t=process.env.CODEVIBE_TMUX_SESSION;if(!t)return r.debug("detectWalkerActive: no CODEVIBE_TMUX_SESSION env \u2014 conservatively assuming walker active",{sessionId:e}),!0;try{let{exec:i}=await import("child_process"),n=w=>new Promise((v,C)=>{i(w,{timeout:3e3},(b,O)=>{b?C(b):v({stdout:O||""})})}),{stdout:s}=await n(`tmux capture-pane -p -e -S -30 -t '${t}'`),a=/\x1B(?:\[[?!0-9;]*[A-Za-z]|\][^\x07\x1B]*(?:\x07|\x1B\\)|[()][A-B012])/g,o=s.split(`
10
- `).map(w=>w.replace(a,"").trimEnd()),c=o.length-1;for(;c>=0&&o[c].trim()==="";)c-=1;if(c<0)return r.warn("detectWalkerActive: pane empty after ANSI strip \u2014 conservatively assuming walker active",{sessionId:e}),!0;let u=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|Chat about this|Type something|Notes: press|\(esc\)|\(shift\+tab\))/i,m=/^\s*(?:[›❯▸▶➜➤]\s*)?\d+\.\s+/,h=-1;for(let w=c;w>=0;w-=1)if(m.test(o[w])){h=w;break}let l=h>=0,f=-1;for(let w=c;w>=0;w-=1)if(u.test(o[w])){f=w;break}let g=f<0?1/0:c-f,p=f>=0&&g<=3,S=f>=0&&h>=0&&f>=h&&f-h<=5,N=l&&p&&S;return r.info("detectWalkerActive",{sessionId:e,walkerActive:N,hasParserBlock:l,chromeNearBottom:p,chromeFollowsBlock:S,chromeDistanceFromBottom:Number.isFinite(g)?g:-1,parserBlockEndsAt:h,lastNonBlank:c,lastChromeLineAt:f,lastLineSample:o[c]?.slice(0,80)??""}),N}catch(i){return r.warn("detectWalkerActive: tmux capture/parse failed \u2014 conservatively assuming walker active",{sessionId:e,error:i instanceof Error?i.message:String(i)}),!0}}getWalkerPosition(e){return e.pendingCurrentQuestion?._isSubmit?"on_synth":e.hasReviewScreen?(e.pendingQuestionsQueue?.length??0)>=2?"nq_mid":"nq_last":"q1"}async emitDegradedAUQNotification(e,t){try{let i=this.encryptForEmit(t,{source:"codevibe_auq_degraded"},d.EventType.ASSISTANT_RESPONSE,e);if(!i)return;let n=i.content,s=i.metadata,a=i.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.ASSISTANT_RESPONSE,source:d.EventSource.DESKTOP,content:n,metadata:s,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e}),isEncrypted:a?!0:void 0}),r.info("Emitted degraded-AUQ notification",{sessionId:e,textPreview:t.slice(0,60)})}catch(i){r.warn("emitDegradedAUQNotification failed",{sessionId:e,error:i instanceof Error?i.message:String(i)})}}async handleMobileReplyAsDismissAndPrompt(e,t,i,n,s){let a=await this.sendVerifiedPromptKey(t,i,"Escape");if(s())return!1;if(!a){try{await this.emitUserChoice(e,"AskUserQuestion dismissal failed (tmux unavailable)")}catch(c){r.warn("emitUserChoice on dismiss-ok1-failed failed",{sessionId:e,error:c instanceof Error?c.message:String(c)})}return!1}if(await new Promise(c=>setTimeout(c,200)),s())return!1;let o=await this.sendFreeFormWhenNoChooser(e,t,n);if(s())return!1;if(!o){try{await this.emitUserChoice(e,"AskUserQuestion dismissed but text-send failed; please retry")}catch(c){r.warn("emitUserChoice on dismiss-ok2-failed failed",{sessionId:e,error:c instanceof Error?c.message:String(c)})}return s()||this.promoteFingerprintAndClearPromptState(t),!1}try{await this.emitUserChoice(e,"\u2192 AskUserQuestion dismissed, sending your reply as a new prompt")}catch(c){r.warn("emitUserChoice on dismiss-success failed \u2014 continuing to promote+clear",{sessionId:e,error:c instanceof Error?c.message:String(c)})}return s()?(r.warn("handleMobileReplyAsDismissAndPrompt: aborted post-emitUserChoice (token rotated); leaving session state for new prompt",{sessionId:e}),!1):(this.promoteFingerprintAndClearPromptState(t),!0)}async handleOptionWithFollowupOnLastQ(e,t,i,n,s,a,o){let c=await this.answerVerifiedInteractivePrompt(t,i,n,{pressEnter:!1});if(o())return!1;if(!c){try{await this.emitUserChoice(e,"Failed to commit Q[N] (tmux unavailable); your reply was not sent")}catch(l){r.warn("emitUserChoice on lastQ-ok1-failed failed",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return!1}if(await new Promise(l=>setTimeout(l,200)),o())return!1;let u=t.pendingQuestionsQueue?.[0];if(!u?._isSubmit)return r.warn("Last-Q flow reached review screen without a bound SYNTH identity",{sessionId:e}),!1;t.pendingCurrentQuestion=u,t.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(u);let m=await this.answerVerifiedInteractivePrompt(t,t.pendingPanePromptIdentity,"1",{pressEnter:!1});if(o())return!1;if(!m){try{await this.emitUserChoice(e,"Q[N] committed but Submit failed (tmux unavailable); please retry on desktop")}catch(l){r.warn("emitUserChoice on lastQ-ok2-failed failed",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return o()||this.promoteFingerprintAndClearPromptState(t),!1}if(await new Promise(l=>setTimeout(l,1500)),o())return!1;let h=await this.sendFreeFormWhenNoChooser(e,t,a);if(o())return!1;h||r.warn("handleOptionWithFollowupOnLastQ: followup text send failed",{sessionId:e});try{await this.emitUserChoice(e,`\u2192 ${s} + sending your reply as a new prompt`)}catch(l){r.warn("emitUserChoice on lastQ-success failed \u2014 continuing to promote+clear",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return o()?(r.warn("handleOptionWithFollowupOnLastQ: aborted post-emitUserChoice (token rotated); leaving session state for new prompt",{sessionId:e}),!1):(this.promoteFingerprintAndClearPromptState(t),!0)}computeAskUserQuestionFingerprint(e){if(!(!e||typeof e!="object"))try{let t=this.stringifyCanonical(e);return(0,j.createHash)("sha256").update(t).digest("hex")}catch(t){r.warn("Failed to fingerprint AskUserQuestion questions",{error:t instanceof Error?t.message:String(t)});return}}stringifyCanonical(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(i=>this.stringifyCanonical(i)).join(",")+"]":"{"+Object.keys(e).sort().map(i=>JSON.stringify(i)+":"+this.stringifyCanonical(e[i])).join(",")+"}"}async markEventExecutedIdempotent(e,t){e.processedEventIds||(e.processedEventIds=new Set),e.processedEventIds.add(t.eventId);try{await this.markEventExecuted(t)}catch(i){r.warn("[walker] markEventExecuted threw \u2014 relying on processedEventIds set for dedup",{sessionId:t.sessionId,eventId:t.eventId,error:String(i)})}}subscribeToOrchestrationPrompts(e){return this.openInlineSubscription({kind:"orchestration-prompt",sessionId:e,query:`subscription OnEventCreated($sessionId: ID!) {
1
+ "use strict";var ze=Object.create;var re=Object.defineProperty;var Ye=Object.getOwnPropertyDescriptor;var Je=Object.getOwnPropertyNames;var Xe=Object.getPrototypeOf,Ze=Object.prototype.hasOwnProperty;var et=(g,e)=>{for(var t in e)re(g,t,{get:e[t],enumerable:!0})},Pe=(g,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Je(e))!Ze.call(g,n)&&n!==t&&re(g,n,{get:()=>e[n],enumerable:!(i=Ye(e,n))||i.enumerable});return g};var K=(g,e,t)=>(t=g!=null?ze(Xe(g)):{},Pe(e||!g||!g.__esModule?re(t,"default",{value:g,enumerable:!0}):t,g)),tt=g=>Pe(re({},"__esModule",{value:!0}),g);var ft={};et(ft,{McpServer:()=>me,classifyApplyUserDecisionEvent:()=>qe,classifyOrchestrationResponse:()=>gt,formatOrchestrationBannerText:()=>He,parseInteractivePromptInput:()=>je,shouldFallThroughForBridgeUnavailable:()=>We,shutdownExitCode:()=>Ge});module.exports=tt(ft);var L=K(require("fs")),G=K(require("path")),j=K(require("os")),Le=require("child_process"),$e=require("util"),Be=require("child_process"),Z=require("crypto");var be=K(require("os")),ke=K(require("path")),Ce=require("@quantiya/codevibe-core"),r=(0,Ce.createLogger)({name:"codevibe-claude",logFile:ke.default.join(be.default.tmpdir(),"codevibe-claude-mcp.log"),level:"info"});var Q=require("node:fs"),Oe=K(require("node:os")),he=K(require("node:path")),oe=require("node:crypto"),Te=448,Re=384,xe=1,te=class extends Error{constructor(t,i,n){super(i);this.claudeSessionId=t;this.code="CLAUDE_SESSION_BINDING_READ_FAILED";this.name="ClaudeSessionBindingReadError",this.cause=n}};function ee(g){return(0,oe.createHash)("sha256").update(g).digest("hex")}function Ae(g,e,t){if(!g||typeof g!="object")return!1;let i=g;return i.version===xe&&i.accountHash===e&&i.claudeSessionId===t&&typeof i.backendSessionId=="string"&&i.backendSessionId.length>0&&i.backendSessionId.length<=256&&/^claude-[A-Za-z0-9._:-]+$/.test(i.backendSessionId)}function ge(g,e,t){if(!g||!e||!t)throw new Error("Cannot derive Claude replacement identity from an empty component");let i=ee(`${g}\0${e}\0${t}`).slice(0,32);return`claude-${e}-r${i}`}var se=class{constructor(e,t){this.logger=e,this.root=t??he.join(Oe.homedir(),".codevibe","claude","backend-session-bindings")}async read(e,t){let i=ee(e),n=this.recordPath(i,t),s;try{s=await Q.promises.readFile(n,"utf8")}catch(c){if(c.code==="ENOENT")return null;throw this.logger.warn("Failed to read durable Claude backend-session binding",{claudeSessionId:t,error:String(c)}),new te(t,`Durable Claude backend-session binding is unavailable for ${t}`,c)}let o;try{o=JSON.parse(s)}catch(c){throw this.logger.warn("Failed to parse durable Claude backend-session binding",{claudeSessionId:t,error:String(c)}),new te(t,`Durable Claude backend-session binding is unreadable for ${t}`,c)}if(!Ae(o,i,t))throw this.logger.warn("Rejected invalid durable Claude backend-session binding",{claudeSessionId:t}),new te(t,`Durable Claude backend-session binding is invalid for ${t}`);return o.backendSessionId}async write(e,t,i){let n=ee(e),s={version:xe,accountHash:n,claudeSessionId:t,backendSessionId:i};if(!Ae(s,n,t))throw new Error("Refusing to persist an invalid Claude backend-session binding");await Q.promises.mkdir(this.root,{recursive:!0,mode:Te}),process.platform!=="win32"&&await Q.promises.chmod(this.root,Te).catch(()=>{});let o=this.recordPath(n,t),c=`${o}.tmp.${process.pid}.${(0,oe.randomBytes)(6).toString("hex")}`,a=null;try{a=await Q.promises.open(c,Q.constants.O_WRONLY|Q.constants.O_CREAT|Q.constants.O_EXCL,Re),await a.writeFile(JSON.stringify(s),"utf8"),await a.sync(),await a.close(),a=null,process.platform!=="win32"&&await Q.promises.chmod(c,Re),await Q.promises.rename(c,o),await this.syncDirectory()}catch(m){throw a&&await a.close().catch(()=>{}),await Q.promises.unlink(c).catch(()=>{}),m}}pathForTesting(e,t){return this.recordPath(ee(e),t)}recordPath(e,t){let i=ee(`${e}\0${t}`);return he.join(this.root,`${i}.json`)}async syncDirectory(){if(process.platform==="win32")return;let e=null;try{e=await Q.promises.open(this.root,Q.constants.O_RDONLY),await e.sync()}catch{}finally{e&&await e.close().catch(()=>{})}}};var p=require("@quantiya/codevibe-core"),Ve=require("@quantiya/quorum-core"),ue=K(require("ws"));var fe=K(require("express")),J=K(require("fs")),ye=K(require("path")),ve=K(require("os")),_e=require("@quantiya/codevibe-core");var R=require("@quantiya/codevibe-core");var ae=class{constructor(e=()=>({status:"healthy"})){this.assignedPort=0;this.healthProvider=e,this.app=(0,fe.default)(),this.setupMiddleware(),this.setupRoutes()}setSessionId(e){this.sessionId=e}getPort(){return this.assignedPort}setupMiddleware(){this.app.use(fe.default.json({limit:"1mb"})),this.app.use((e,t,i)=>{r.debug(`${e.method} ${e.path}`,{body:e.body,query:e.query}),i()}),this.app.use((e,t,i,n)=>{r.error("Express error:",e);let s={success:!1,error:e.message||"Internal server error"};i.status(500).json(s)})}setupRoutes(){this.app.get("/health",this.handleHealth.bind(this)),this.app.post("/event",this.handleEvent.bind(this)),process.env.NODE_ENV!=="production"&&this.app.post("/test/execute",this.handleTestExecute.bind(this))}handleHealth(e,t){let i=this.healthProvider();t.json({success:!0,service:"codevibe-claude",data:{...i,uptime:process.uptime(),version:"0.1.0",timestamp:new Date().toISOString()}})}async handleEvent(e,t){try{let i=e.body;if(!i.session_id){let o={success:!1,error:"Missing required field: session_id"};t.status(400).json(o);return}if(!i.hook_event_name){let o={success:!1,error:"Missing required field: hook_event_name"};t.status(400).json(o);return}let n=this.transformHookToEvent(i);r.info("Received event from hook",{sessionId:i.session_id,hookEvent:i.hook_event_name,type:n.type}),this.eventHandler?await this.eventHandler(n):r.warn("No event handler registered");let s={success:!0,message:"Event processed successfully"};t.json(s)}catch(i){r.error("Error handling event:",i);let n={success:!1,error:i instanceof Error?i.message:"Unknown error"};t.status(500).json(n)}}async handleTestExecute(e,t){try{let{sessionId:i,prompt:n}=e.body;if(!i||!n){let o={success:!1,error:"Missing required fields: sessionId, prompt"};t.status(400).json(o);return}r.info("Test execute request",{sessionId:i,prompt:n});let s={success:!0,message:"Test execution endpoint - not implemented yet",data:{sessionId:i,prompt:n}};t.json(s)}catch(i){r.error("Error in test execute:",i);let n={success:!1,error:i instanceof Error?i.message:"Unknown error"};t.status(500).json(n)}}transformHookToEvent(e){let t,i,n={cwd:e.cwd,hook_event_name:e.hook_event_name,...e.metadata||{}};if(e.type&&e.content!==void 0)t=e.type,i=e.content;else switch(e.hook_event_name){case"SessionStart":t=R.EventType.NOTIFICATION,i="Session started",n.source=e.source;break;case"SessionEnd":t=R.EventType.NOTIFICATION,i=`Session ended: ${e.reason||"unknown"}`,n.reason=e.reason;break;case"UserPromptSubmit":t=R.EventType.USER_PROMPT,i=e.prompt||"";break;case"PostToolUse":t=R.EventType.TOOL_USE,i=JSON.stringify({tool_name:e.tool_name,tool_input:e.tool_input,tool_response:e.tool_response}),n.tool_name=e.tool_name;break;case"Notification":t=R.EventType.NOTIFICATION,i=e.message||"",n.notification_type=e.notification_type;break;default:t=R.EventType.NOTIFICATION,i=`Hook event: ${e.hook_event_name}`}return{session_id:e.session_id,hook_event_name:e.hook_event_name,type:t,source:R.EventSource.DESKTOP,content:i,metadata:n,tool_use_id:e.tool_use_id,tool_input:e.tool_input,transcript_path:e.transcript_path}}onEvent(e){this.eventHandler=e}async start(e){let t=e||this.sessionId;return t&&(this.sessionId=t),new Promise((i,n)=>{try{let s=(0,_e.getConfig)(),o=s.server.dynamicPort?0:s.server.port;this.server=this.app.listen(o,s.server.host,()=>{let c=this.server.address();this.assignedPort=c.port,r.info(`HTTP API listening on http://${s.server.host}:${this.assignedPort}`),this.sessionId&&this.writePortFile(this.sessionId,this.assignedPort),i(this.assignedPort)}),this.server.on("error",c=>{r.error("HTTP server error:",c),n(c)})}catch(s){n(s)}})}writePortFile(e,t){let i=ye.join(ve.tmpdir(),`codevibe-claude-${e}.port`);try{J.writeFileSync(i,t.toString()),r.info(`Port file written: ${i} -> ${t}`)}catch(n){r.error(`Failed to write port file: ${i}`,n)}}removePortFile(){if(this.sessionId){let e=ye.join(ve.tmpdir(),`codevibe-claude-${this.sessionId}.port`);try{J.existsSync(e)&&(J.unlinkSync(e),r.info(`Port file removed: ${e}`))}catch(t){r.warn(`Failed to remove port file: ${e}`,t)}}}async stop(e){return new Promise((t,i)=>{this.sessionId&&e?.protectedSessionIds?.has(this.sessionId)?r.info("Skipping port file removal \u2014 another daemon still serves this session",{sessionId:this.sessionId}):this.removePortFile(),this.server?this.server.close(n=>{n?(r.error("Error stopping HTTP server:",n),i(n)):(r.info("HTTP API stopped"),t())}):t()})}};var Fe=require("child_process"),Ne=require("@quantiya/codevibe-core");var ce=class{async executePrompt(e,t){let i=(0,Ne.getConfig)(),n=i.claude.defaultTimeout;return r.info("Executing prompt from mobile",{sessionId:e,promptLength:t.length,timeout:n}),new Promise(s=>{let o=["--resume",e,"--print","--output-format","stream-json",t];r.debug("Spawning Claude command",{command:i.claude.command,args:o});let c=(0,Fe.spawn)(i.claude.command,o,{stdio:["pipe","pipe","pipe"],shell:!0}),a="",m="",d=!1,w=setTimeout(()=>{d=!0,r.warn("Command execution timed out",{sessionId:e,timeout:n}),c.kill("SIGTERM")},n);c.stdout?.on("data",l=>{let v=l.toString();a+=v,r.debug("Command stdout",{output:v.slice(0,200)})}),c.stderr?.on("data",l=>{let v=l.toString();m+=v,r.debug("Command stderr",{output:v.slice(0,200)})}),c.on("close",l=>{clearTimeout(w);let v={success:l===0&&!d,output:a,error:m,exitCode:l||void 0,timedOut:d};v.success?r.info("Command executed successfully",{sessionId:e,exitCode:l,outputLength:a.length}):r.error("Command execution failed",{sessionId:e,exitCode:l,timedOut:d,error:m.slice(0,500)}),s(v)}),c.on("error",l=>{clearTimeout(w),r.error("Failed to spawn command",{error:l.message}),s({success:!1,error:l.message,timedOut:!1})})})}detectInteractivePrompt(e){return[/\[Y\/n\]/i,/\[y\/N\]/i,/\(y\/n\)/i,/Continue\?/i,/Proceed\?/i].some(i=>i.test(e))}extractPromptText(e){let t=e.split(`
2
+ `);for(let i=t.length-1;i>=0;i--){let n=t[i].trim();if(this.detectInteractivePrompt(n))return n}return null}};var Ue=require("child_process"),Me=require("util");var we=(0,Me.promisify)(Ue.exec),de=class{async answerInteractivePrompt(e,t,i={}){let{pressEnter:n=!0}=i;r.info("Attempting to answer interactive prompt",{sessionId:e,response:t,pressEnter:n});try{let s=process.env.CODEVIBE_TMUX_SESSION;return r.info("Checking tmux session environment",{tmuxSession:s||"(not set)",allEnvKeys:Object.keys(process.env).filter(o=>o.includes("CODEVIBE")||o.includes("TMUX"))}),s?(r.info("Using tmux send-keys",{tmuxSession:s,pressEnter:n}),await this.sendViaTmux(s,t,n),r.info("Successfully sent response to interactive prompt",{sessionId:e,response:t,pressEnter:n}),!0):(r.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:e,hint:"Start Claude Code using `codevibe --agent claude`"}),!1)}catch(s){return r.error("Failed to answer interactive prompt",{sessionId:e,error:s instanceof Error?s.message:String(s)}),!1}}async sendViaTmux(e,t,i){let n=t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\$/g,"\\$").replace(/`/g,"\\`");r.info("Sending via tmux",{sessionName:e,inputLength:t.length,pressEnter:i});try{let s=`tmux send-keys -t "${e}" -l "${n}"`,o=await we(s);if(r.info("tmux send-keys (text) completed",{stdout:o.stdout||"(empty)",stderr:o.stderr||"(empty)"}),i){await this.delay(500);let c=`tmux send-keys -t "${e}" Enter`,a=await we(c);r.info("tmux send-keys (Enter) completed",{stdout:a.stdout||"(empty)",stderr:a.stderr||"(empty)"})}else r.info("tmux send-keys: skipping Enter (caller requested digit-only)")}catch(s){throw r.error("tmux send-keys failed",{sessionName:e,error:s}),s}}async sendKey(e,t){let i=process.env.CODEVIBE_TMUX_SESSION;if(!i)return r.error("No tmux session found for sendKey",{sessionId:e,keyName:t}),!1;try{let n=`tmux send-keys -t "${i}" ${t}`,s=await we(n);return r.info("tmux send-keys (single key) completed",{sessionId:e,keyName:t,stdout:s.stdout||"(empty)",stderr:s.stderr||"(empty)"}),!0}catch(n){return r.error("tmux send-keys (single key) failed",{sessionId:e,keyName:t,error:n instanceof Error?n.message:String(n)}),!1}}delay(e){return new Promise(t=>setTimeout(t,e))}isPromptResponse(e){let t=e.trim().toLowerCase();return!!(t==="y"||t==="n"||t==="yes"||t==="no"||/^[0-9]+$/.test(t)||/^[a-z]$/.test(t)||["exit","quit","q","continue","skip","abort","retry","cancel"].includes(t))}};var D=require("@quantiya/codevibe-core"),De=3e3,nt=8,it=10,rt=100;function Ke(g){let e=Buffer.byteLength(g.carryover);return{status:"partial",progressKey:`${g.fileIdentity.dev}:${g.fileIdentity.ino}:${g.size}:${g.endOfComplete}:${e}`,size:g.size,carryoverBytes:e,endOfComplete:g.endOfComplete}}var st="tool_activity",pe=class{constructor(e){this.consolidator=null;this.ledger=null;this.cursor=null;this.backendSessionId=null;this.transcriptPath=null;this.backstopTimer=null;this.backstopInFlight=null;this.deps=e}get sessionId(){return this.backendSessionId}isActive(){return this.consolidator!==null}async start(e,t){this.backendSessionId=e,this.transcriptPath=t??null;let i=new D.ToolActivityOutbox({agent:"claude",logger:this.deps.logger,root:this.deps.root});this.ledger=new D.ToolActivityLedger({agent:"claude",sessionId:e,logger:this.deps.logger,root:this.deps.root}),t&&(this.cursor=new D.ToolReplayCursor({agent:"claude",sessionId:e,canonicalPath:t,logger:this.deps.logger,root:this.deps.root,readBytes:this.deps.replayReadBytes,maxFrameBytes:this.deps.replayMaxFrameBytes}),await this.cursor.load()),this.consolidator=new D.ToolActivityConsolidator({agent:"claude",sessionId:e,logger:this.deps.logger,outbox:i,ledger:this.ledger,transport:this.buildTransport()}),await this.consolidator.init(),this.cursor&&await this.consolidator.repairLostWindows(this.cursor);let n=this.deps.backstopIntervalMs??3e3;this.cursor&&n>0&&(this.backstopTimer=setInterval(()=>{this.runTranscriptBackstop().catch(s=>this.deps.logger.warn("[tool-activity] transcript backstop failed",{sessionId:e,err:String(s)}))},n),this.backstopTimer.unref?.()),this.deps.logger.info("[tool-activity] consolidator started",{sessionId:e,hasTranscript:!!t})}async observeHookTool(e){if(!this.consolidator||!e.toolUseId||!e.toolName)return;let t=this.consolidator,i={id:e.toolUseId,tool:e.toolName,normalizedTarget:Qe(e.toolName,e.toolInput),ts:e.ts??new Date().toISOString(),byteOffset:0,...e.toolInput!==void 0?{digest:(0,D.digestOf)(e.toolInput)}:{}},n=await t.observe(i);return n==="closed"?await t.captureTerminal(i,this.deps.shutdownDrainMs??De)?"sealed-terminal":"closed":n}async runTranscriptBackstop(){if(this.backstopInFlight)return this.backstopInFlight;if(!this.consolidator||!this.cursor)return;let e=this.doTranscriptBackstop().then(()=>{});return this.backstopInFlight=e,e.catch(()=>{}).finally(()=>{this.backstopInFlight===e&&(this.backstopInFlight=null)}),e}async doTranscriptBackstop(e=!1){let t=this.consolidator,i=this.cursor;if(!t||!i)return"caught-up";this.ledger&&await this.ledger.reloadLegacyOwned();for(let n=0;n<nt;n+=1){let s=await i.readFrames();if(s.missing)return"caught-up";if(s.frames.length===0)return s.carryover.length>0?Ke(s):"caught-up";let o=Number.POSITIVE_INFINITY,c=e?{includeInFlight:!0}:void 0;for(let a of s.frames)for(let m of ot(a.line,a.startOffset))await t.observe(m,c)==="deferred-in-flight"&&(o=Math.min(o,a.startOffset));if(await t.flush(),o<s.endOfComplete)return await i.checkpoint(o,{fileIdentity:s.fileIdentity,size:s.size,carryover:""}),"more";if(await i.checkpoint(s.endOfComplete,{fileIdentity:s.fileIdentity,size:s.size,carryover:s.carryover}),!s.hasMore)return s.carryover.length>0?Ke(s):"caught-up"}return"more"}async flush(){await this.consolidator?.flush()}async stop(){this.backstopTimer&&(clearInterval(this.backstopTimer),this.backstopTimer=null),await this.runTranscriptBackstop().catch(()=>{});let e=Math.max(1,Math.trunc(this.deps.shutdownPartialNoProgressTurns??rt)),t=null,i=0;for(;;){let n=await this.doTranscriptBackstop(!0);if(n==="caught-up")break;if(typeof n!="string"){if(n.progressKey===t?i+=1:(t=n.progressKey,i=1),i>=e){this.deps.logger.warn("[tool-activity] deferring unchanged incomplete transcript suffix during shutdown; the next owner will re-read it from the durable complete-line boundary",{size:n.size,carryoverBytes:n.carryoverBytes,endOfComplete:n.endOfComplete,unchangedTurns:i});break}await new Promise(s=>setTimeout(s,it))}else t=null,i=0,await new Promise(s=>setImmediate(s))}await this.consolidator?.drainSends(this.deps.shutdownDrainMs??De).catch(()=>{}),await this.consolidator?.shutdown(),this.consolidator=null,this.ledger=null,this.cursor=null}buildTransport(){let e=async(t,i,n)=>{let s=n?n.sessionKey:this.deps.getSessionKey();if(!s)throw new Error("[tool-activity] no session key at send (retryable)");let o=n?n.sessionKeyGen:this.deps.getSessionKeyGen();await this.deps.createEvent({sessionId:t,type:D.EventType.TOOL_ACTIVITY,source:D.EventSource.DESKTOP,content:this.deps.encryptContent(st,s),metadata:{encrypted:this.deps.encryptMetadata({manifest:i.manifest},s)},timestamp:i.windowStart,isEncrypted:!0,clientEventId:i.clientEventId,...o?{expectedSessionKeyGen:o}:{}})};return{send:async(t,i)=>{try{await e(t,i)}catch(n){if((0,D.isSessionKeyStaleError)(n)&&this.deps.refreshSessionKey){let s=await this.deps.refreshSessionKey(t);if(!s)throw n;await e(t,i,s);return}throw n}}}}};function ot(g,e){let t;try{t=JSON.parse(g)}catch{return[]}let i=t?.message?.content;if(t?.type!=="assistant"||!Array.isArray(i))return[];let n=[];for(let s of i)s?.type==="tool_use"&&typeof s.id=="string"&&typeof s.name=="string"&&n.push({id:s.id,tool:s.name,normalizedTarget:Qe(s.name,s.input),ts:typeof t.timestamp=="string"?t.timestamp:new Date(0).toISOString(),byteOffset:e,...s.input!==void 0?{digest:(0,D.digestOf)(s.input)}:{}});return n}function Qe(g,e){if(!e||typeof e!="object")return;let t=e,i=n=>typeof n=="string"&&n.length>0?n:void 0;return i(t.file_path)??i(t.path)??i(t.pattern)??i(t.notebook_path)??i(t.url)??(typeof t.command=="string"?t.command.trim().split(/\s+/)[0]:void 0)}var le=(0,$e.promisify)(Be.exec),at="/exit",X="CODEVIBE_TMUX_SESSION",ct=1800*1e3,dt=60*1e3,pt=1440*60*1e3;async function lt(g,e){let t=async(i,n)=>{try{await le(i)}catch(s){r.warn("tmux send-keys failed during self-terminate",{sessionName:g,label:n,error:String(s)})}};await t(`tmux send-keys -t "${g}" C-c`,"ctrl-c"),await new Promise(i=>setTimeout(i,200)),await t(`tmux send-keys -t "${g}" -l "${e}"`,"quit-text"),await new Promise(i=>setTimeout(i,500)),await t(`tmux send-keys -t "${g}" Enter`,"enter")}var ut={question:"Ready to submit your answers?",options:[{label:"Submit answers",description:"Send your selections to the assistant"},{label:"Cancel",description:"Discard your answers"}],multiSelect:!1,_isSubmit:!0};function mt(g){let e=g.length,t=e>=2,i=g.slice(1);return t&&i.push({...ut}),{questionCount:e,hasReviewScreen:t,remainingQueue:i}}var me=class g{constructor(e){this.activeSessions=new Map;this.assignedPort=0;this.portRefreshTimer=null;this.e1TtlRefreshTimer=null;this.encryptedKeyRecoveryTimer=null;this.encryptedKeyRecoveryAttempt=0;this.encryptedKeyRecovery=null;this.sessionKey=null;this.sessionKeyGen=null;this.sessionIsEncrypted=!1;this.encryptionResolved=!0;this.toolActivity=null;this.toolActivityLegacyRecorders=new Map;this.claudeToBackendSessionId=new Map;this.sessionBindingStore=new se(r);this.sessionStartChain=Promise.resolve();this.sessionLifecycleQueueDepth=0;this.retiredSessionRecoveryPromises=new Map;this.lifecycleGeneration=0;this.isStopping=!1;this.currentClaudeSessionId=null;this.claudeOwnershipGeneration=0;this.claudeSessionIntentVersions=new Map;this.endedOrSupersededClaudeSessionIds=new Set;this.nestedClaudeSessionIds=new Set;this.pendingMobilePrompts=new Map;this.e1PromptRaises=new Map;this.e1NeedsReRaise=new Set;this.e1ParkedOrphans=new Map;this.nextPromptGen=1;this.httpApi=new ae(()=>this.getDaemonHealth()),this.commandExecutor=new ce,this.promptResponder=new de,this.initialSessionId=e,this.currentClaudeSessionId=e??null}static{this.E1_TTL_REFRESH_MS=300*1e3}static{this.MOBILE_PROMPT_EXPIRY_MS=3e3}newE1Raise(e,t,i){let{record:n}=(0,p.newPromptRaise)({sessionId:e,producerKind:"PLUGIN_APPROVAL",mobileActionable:t,agentType:"CLAUDE",...i!==void 0&&{title:i}});return this.e1PromptRaises.set(n.promptId,{record:n}),n}stashE1RaiseContent(e,t,i){let n=this.e1PromptRaises.get(e);n&&(n.contentPlain=t,n.metadataPlain=i)}snapshotOpenE1(e){return[...this.e1PromptRaises.values()].filter(t=>t.record.sessionId===e).map(t=>({record:{...t.record},...t.contentPlain!==void 0&&{contentPlain:t.contentPlain},...t.metadataPlain!==void 0&&{metadataPlain:t.metadataPlain}}))}async reRaiseSnapshotE1(e,t,i){if(e.length===0)return[];r.info("E1: re-raising open prompts onto replacement session",{replacementSessionId:t,count:e.length});let n=[];for(let s of e){let o={record:{...s.record,sessionId:t},...s.contentPlain!==void 0&&{contentPlain:s.contentPlain},...s.metadataPlain!==void 0&&{metadataPlain:s.metadataPlain}};this.e1PromptRaises.set(o.record.promptId,o),await this.reRaiseOneE1(t,o,i?{untilAck:i}:void 0)||n.push(s)}return n}parkE1Orphans(e,t){if(t.length===0)return;let i=new Map;for(let n of this.e1ParkedOrphans.get(e)??[])i.set(n.record.promptId,n);for(let n of t)i.set(n.record.promptId,n);this.e1ParkedOrphans.set(e,[...i.values()]),r.info("E1: parked retired-session open prompts for replacement re-home",{claudeSessionId:e,count:i.size})}async drainParkedE1Orphans(e,t,i){let n=this.e1ParkedOrphans.get(e);if(!n||n.length===0)return;let s=await this.reRaiseSnapshotE1(n,t,i);if(s.length===0){this.e1ParkedOrphans.delete(e);return}this.e1ParkedOrphans.set(e,s),r.info("E1: retirement drain aborted before ACK \u2014 retained durable park for next bootstrap",{claudeSessionId:e,liveSessionId:t,retained:s.length})}async reRaiseOneE1(e,t,i){let n=t.record,s=n.mobileActionable&&t.contentPlain!==void 0,o=i?.untilAck,c=4;for(let a=1;;a++){if(o&&!o())return this.e1NeedsReRaise.add(n.promptId),r.info("E1: re-raise wait aborted (lifecycle superseded/stopping) \u2014 flagged for TTL-drain",{sessionId:e,promptId:n.promptId}),!1;try{if(s){let m=this.encryptForEmit(t.contentPlain,t.metadataPlain??{},p.EventType.INTERACTIVE_PROMPT,e);if(!m)return!1;await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.INTERACTIVE_PROMPT,source:p.EventSource.DESKTOP,content:m.content,metadata:m.metadata,...(0,p.raiseFieldsFromRecord)(n),timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:m.isEncrypted?!0:void 0})}else if(n.mobileActionable=!1,!await this.emitSuppressedPromptCarrier(e,n))return!1;return this.e1NeedsReRaise.delete(n.promptId),r.info("E1: re-raised prompt onto replacement session",{sessionId:e,promptId:n.promptId,actionable:s}),!0}catch(m){if(!o&&a>=c)return this.e1NeedsReRaise.add(n.promptId),r.error("E1: re-raise failed after retries \u2014 flagged for TTL-drain re-attempt",{sessionId:e,promptId:n.promptId,error:m instanceof Error?m.message:String(m)}),!1;if(o){let d=Date.now()+Math.min(250*2**(a-1),4e3);for(;Date.now()<d&&o();)await new Promise(w=>setTimeout(w,Math.min(50,d-Date.now())))}else await new Promise(d=>setTimeout(d,250*a))}}}async drainPendingE1ReRaises(){for(let e of[...this.e1NeedsReRaise]){let t=this.e1PromptRaises.get(e);if(!t){this.e1NeedsReRaise.delete(e);continue}this.activeSessions.has(t.record.sessionId)&&await this.reRaiseOneE1(t.record.sessionId,t)}}getDaemonHealth(){let e=this.encryptedKeyRecovery;return e?{status:"degraded",reason:"encrypted_session_key_unavailable",sessionId:e.backendSessionId,retryAttempt:this.encryptedKeyRecoveryAttempt}:{status:"healthy"}}clearEncryptedKeyRecovery(e,t,i){t&&this.encryptedKeyRecovery?.claudeSessionId!==t||i&&this.encryptedKeyRecovery?.backendSessionId!==i||(this.encryptedKeyRecoveryTimer&&(clearTimeout(this.encryptedKeyRecoveryTimer),this.encryptedKeyRecoveryTimer=null),this.encryptedKeyRecovery&&r.info("Encrypted-session key recovery cleared",{claudeSessionId:this.encryptedKeyRecovery.claudeSessionId,backendSessionId:this.encryptedKeyRecovery.backendSessionId,reason:e}),this.encryptedKeyRecovery=null,this.encryptedKeyRecoveryAttempt=0)}enterEncryptedKeyRecovery(e,t){let i=e.session_id,n=this.encryptedKeyRecovery;return!n||n.claudeSessionId!==i||n.backendSessionId!==t?(this.clearEncryptedKeyRecovery("replaced by a different recovery target"),this.encryptedKeyRecovery={claudeSessionId:i,backendSessionId:t,payload:e,livenessInvalidated:!1}):(n.payload=e,n.livenessInvalidated=!1,this.encryptedKeyRecoveryTimer&&(clearTimeout(this.encryptedKeyRecoveryTimer),this.encryptedKeyRecoveryTimer=null)),this.encryptedKeyRecovery}async invalidateEncryptedRecoveryLiveness(e){for(;!this.isStopping&&this.encryptedKeyRecovery===e;){let t=await this.appSyncClient.invalidateHeartbeat(e.backendSessionId);if(this.isStopping||this.encryptedKeyRecovery!==e)return!1;if(t)return e.livenessInvalidated=!0,this.scheduleEncryptedKeyRecovery(e.payload,e.backendSessionId),!0;r.warn("Retrying durable invalidation before encrypted-key recovery",{claudeSessionId:e.claudeSessionId,backendSessionId:e.backendSessionId}),await new Promise(i=>setTimeout(i,1e3))}return!1}scheduleEncryptedKeyRecovery(e,t){if(this.isStopping)return;let i=e.session_id,n=this.encryptedKeyRecovery;if(!n||n.claudeSessionId!==i||n.backendSessionId!==t){let c=this.enterEncryptedKeyRecovery(e,t);this.invalidateEncryptedRecoveryLiveness(c);return}else n.payload=e;if(!n.livenessInvalidated||this.encryptedKeyRecoveryTimer)return;let s=Math.min(6e4,5e3*2**Math.min(this.encryptedKeyRecoveryAttempt,4));this.encryptedKeyRecoveryAttempt+=1;let o=this.encryptedKeyRecovery;o&&(r.warn("Encrypted session is keyless; scheduling automatic resume recovery",{claudeSessionId:i,backendSessionId:t,attempt:this.encryptedKeyRecoveryAttempt,delayMs:s}),this.encryptedKeyRecoveryTimer=setTimeout(()=>{this.encryptedKeyRecoveryTimer=null,!(this.isStopping||this.encryptedKeyRecovery!==o)&&this.handleSessionStart(o.payload,o.backendSessionId,"self-heal").catch(c=>{r.warn("Encrypted-session key recovery attempt failed",{claudeSessionId:i,backendSessionId:t,error:String(c)})}).finally(()=>{!this.isStopping&&this.encryptedKeyRecovery===o&&!this.encryptedKeyRecoveryTimer&&this.scheduleEncryptedKeyRecovery(o.payload,o.backendSessionId)})},s),this.encryptedKeyRecoveryTimer.unref?.())}getPort(){return this.assignedPort}generateBackendSessionId(e){return`claude-${e}`}trackMobilePrompt(e,t){this.pendingMobilePrompts.has(e)||this.pendingMobilePrompts.set(e,[]),this.pendingMobilePrompts.get(e).push({prompt:t.trim(),timestamp:Date.now()}),r.debug("Tracking mobile prompt for deduplication",{sessionId:e,promptLength:t.length})}isRecentMobilePrompt(e,t){let i=this.pendingMobilePrompts.get(e);if(!i)return!1;let n=Date.now(),s=t.trim(),o=[],c=!1;for(let a of i)if(!(n-a.timestamp>g.MOBILE_PROMPT_EXPIRY_MS)){if(!c&&a.prompt===s){c=!0,r.debug("Found matching mobile prompt, filtering duplicate",{sessionId:e});continue}o.push(a)}return o.length>0?this.pendingMobilePrompts.set(e,o):this.pendingMobilePrompts.delete(e),c}writePortFile(e){let t=G.join(j.tmpdir(),`codevibe-claude-${e}.port`);try{L.writeFileSync(t,this.assignedPort.toString()),r.info(`Port file written: ${t} -> ${this.assignedPort}`)}catch(i){r.error(`Failed to write port file: ${t}`,i)}}removePortFile(e){let t=G.join(j.tmpdir(),`codevibe-claude-${e}.port`);try{L.existsSync(t)&&(L.unlinkSync(t),r.info(`Port file removed: ${t}`))}catch(i){r.warn(`Failed to remove port file: ${t}`,i)}}hasOtherLiveDaemonForSession(e){try{let t=(0,Le.execSync)("ps -eww -o pid= -o args=",{encoding:"utf8",timeout:2e3}),i=process.pid;for(let n of t.split(`
3
+ `)){let s=n.trim();if(!s)continue;let o=s.indexOf(" ");if(o<0)continue;let c=parseInt(s.substring(0,o),10);if(isNaN(c)||c===i)continue;let a=s.substring(o+1);if(/node.*codevibe-claude.*server\.js/.test(a)&&a.includes(e))return!0}return!1}catch(t){return r.warn('hasOtherLiveDaemonForSession: ps query failed; falling back to "no other daemon"',{error:String(t)}),!1}}async start(){try{if(r.info("Starting CodeVibe MCP Server...",{environment:(0,p.getEnvironment)()}),this.appSyncClient=new p.AppSyncClient,await this.appSyncClient.authenticateWithStoredTokens()){r.info("Authenticated with stored OAuth tokens",{userId:this.appSyncClient.getCurrentUserId(),email:this.appSyncClient.getCurrentUserEmail()}),await(0,p.registerDeviceEncryptionKey)(this.appSyncClient,r),(0,p.startDeviceKeyWatcher)(this.appSyncClient,r),(0,p.pushDetectedAgents)(this.appSyncClient,r).catch(t=>{r.warn("Failed to update available agents (non-fatal)",{error:t?.message})});try{let t=await this.appSyncClient.sweepOrphanSessions({agentType:"CLAUDE"});t>0&&r.info("Orphan sweep: marked stale Claude sessions INACTIVE",{swept:t})}catch(t){r.warn("Orphan sweep failed, continuing startup",{error:t instanceof Error?t.message:String(t)})}}else r.error('Authentication failed. Run "codevibe login" first.'),console.error('Not authenticated. Run "codevibe login" to sign in.'),process.exit(1);this.httpApi.onEvent(this.handleEventFromHook.bind(this)),this.assignedPort=await this.httpApi.start(this.initialSessionId),this.startPortKeepalive(),r.info("MCP Server started successfully",{port:this.assignedPort,host:(0,p.getConfig)().server.host,dynamicPort:(0,p.getConfig)().server.dynamicPort,sessionId:this.initialSessionId,authenticated:this.appSyncClient.isAuthenticated(),userId:this.appSyncClient.getCurrentUserId()})}catch(e){throw r.error("Failed to start MCP Server:",e),e}}startPortKeepalive(){this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null);let e=Number(process.env.CODEVIBE_PORTFILE_REFRESH_MS),t=Number.isFinite(e)&&e>0?Math.min(pt,Math.max(dt,e)):ct;this.portRefreshTimer=setInterval(()=>this.refreshPortFiles(),t),this.e1TtlRefreshTimer&&clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=setInterval(()=>{this.refreshE1Ttls()},g.E1_TTL_REFRESH_MS)}async refreshE1Ttls(){await this.drainPendingE1ReRaises();let e=[...this.e1PromptRaises.values()].filter(t=>this.activeSessions.has(t.record.sessionId)).map(t=>t.record.promptId);if(e.length!==0)try{let t=await this.appSyncClient.refreshOpenPromptTtl(e);for(let i of t){let n=this.e1PromptRaises.get(i);n&&this.activeSessions.has(n.record.sessionId)&&await this.reRaiseOneE1(n.record.sessionId,n)}}catch(t){r.warn("E1: TTL-refresh tick failed (non-fatal, retries next tick)",{error:t instanceof Error?t.message:String(t)})}}refreshPortFiles(){let e=new Date;for(let i of this.activeSessions.values()){let n=G.join(j.tmpdir(),`codevibe-claude-${i.claudeSessionId}.port`);try{L.utimesSync(n,e,e)}catch(o){o?.code==="ENOENT"?this.writePortFile(i.claudeSessionId):r.warn(`Port-file refresh failed: ${n}`,o)}let s=G.join(j.tmpdir(),`codevibe-claude-${i.claudeSessionId}.pid`);try{L.utimesSync(s,e,e)}catch(o){o?.code!=="ENOENT"&&r.warn(`Pid-file refresh failed: ${s}`,o)}}let t=process.env[X];if(t){let i=G.join(j.tmpdir(),`codevibe-claude-instance-${t}.json`);try{L.utimesSync(i,e,e)}catch(n){n?.code!=="ENOENT"&&r.warn(`Instance-map refresh failed: ${i}`,n)}}}async stop(){r.info("Stopping MCP Server..."),this.isStopping=!0,this.lifecycleGeneration+=1,this.clearEncryptedKeyRecovery("daemon stopping"),await this.sessionStartChain.catch(()=>{}),this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null),this.e1TtlRefreshTimer&&(clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=null),this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null);let e=Array.from(this.activeSessions.keys()),t=new Set;r.info(`Marking ${e.length} active session(s) as INACTIVE...`);for(let i of e){let n=this.activeSessions.get(i);n?.mobileEndWatcher&&(n.mobileEndWatcher.stop(),n.mobileEndWatcher=void 0),this.appSyncClient.stopHeartbeat(i)}for(let i of e){let n=this.activeSessions.get(i);try{if(n&&this.hasOtherLiveDaemonForSession(n.claudeSessionId)){r.info("Another daemon serves this session \u2014 skipping mark INACTIVE AND port file removal during shutdown",{sessionId:i,claudeSessionId:n.claudeSessionId,myPid:process.pid}),t.add(n.claudeSessionId);continue}try{await this.appSyncClient.updateSession({sessionId:i,status:p.SessionStatus.INACTIVE}),r.info("Session marked as INACTIVE during shutdown",{sessionId:i})}catch(s){r.warn("Failed to mark session as INACTIVE during shutdown",{sessionId:i,error:s})}n&&this.removePortFile(n.claudeSessionId)}catch(s){r.warn("Failed during shutdown cleanup",{sessionId:i,error:s})}}this.appSyncClient.cleanupSubscriptions(),this.activeSessions.clear(),await this.httpApi.stop({protectedSessionIds:t}),r.info("MCP Server stopped")}encryptForEmit(e,t,i,n){let s=this.activeSessions.get(n)?.encryptionSnapshot;if(!s)return r.error("No encryption snapshot for target session \u2014 dropping event (fail-closed, #638 H1)",{type:i,sessionId:n,sessionKnown:this.activeSessions.has(n)}),null;let o=(0,p.encryptForEmit)(s.sessionKey,s.sessionIsEncrypted,e,t,s.encryptionResolved);return o===null&&r.error("No session key for ENCRYPTED/unresolved session \u2014 dropping event (fail-closed, #638)",{type:i,sessionId:n,encryptionResolved:s.encryptionResolved}),o}async handleEventFromHook(e){let{session_id:t,hook_event_name:i,type:n,content:s}=e;if(r.info("Processing hook event",{sessionId:t,hookEvent:i,type:n}),i==="SessionStart"&&e.metadata?.source==="startup"&&this.currentClaudeSessionId!==null&&this.currentClaudeSessionId!==t&&this.nestedClaudeSessionIds.add(t),this.nestedClaudeSessionIds.has(t)){r.info("Ignoring hook from quarantined nested Claude session",{sessionId:t,hookEvent:i,owningSessionId:this.currentClaudeSessionId});return}let o,c;try{if(i==="SessionStart")await this.handleSessionStart(e);else if(i==="SessionEnd")await this.handleSessionEnd(e);else if(!this.activeSessions.has(this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t))){r.warn("Hook event for un-bootstrapped session \u2014 self-healing via SessionStart (#638)",{claudeSessionId:t,hookEvent:i});let h=this.encryptedKeyRecovery?.claudeSessionId===t?this.encryptedKeyRecovery.backendSessionId:void 0;await this.handleSessionStart({session_id:t,metadata:e.metadata,transcript_path:e.transcript_path},h,"self-heal");let I=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);if(!this.activeSessions.has(I)){r.warn("Dropping hook because Claude self-heal lifecycle intent is stale",{claudeSessionId:t,hookEvent:i});return}}let a=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);if(this.toolActivity?.isActive()&&(n===p.EventType.USER_PROMPT||n===p.EventType.ASSISTANT_RESPONSE||n===p.EventType.INTERACTIVE_PROMPT||n===p.EventType.NOTIFICATION)&&await this.toolActivity.flush().catch(h=>r.warn("[tool-activity] boundary flush failed",{sessionId:a,err:String(h)})),i==="UserPromptSubmit"){let h=this.activeSessions.get(a);if(h?.completedAskUserQuestionFingerprints?.size){let I=h.completedAskUserQuestionFingerprints.size;h.completedAskUserQuestionFingerprints.clear(),r.info("Turn boundary \u2014 cleared closed-AskUserQuestion fingerprints",{sessionId:a,clearedCount:I})}}if(n===p.EventType.USER_PROMPT&&e.source===p.EventSource.DESKTOP&&i==="UserPromptSubmit"&&s&&this.isRecentMobilePrompt(a,s)){r.info("Skipping duplicate USER_PROMPT from mobile-originated prompt",{sessionId:a,contentLength:s.length});return}if(n===p.EventType.INTERACTIVE_PROMPT){(typeof e.prompt_id!="string"||e.prompt_id.length===0)&&(e.prompt_id=`synth-${(0,Z.randomUUID)()}`,r.info("Synthesized prompt_id for INTERACTIVE_PROMPT (hook omitted it)",{sessionId:a,synthesizedPromptId:e.prompt_id}));let h=this.activeSessions.get(a),I;if(h&&e.metadata?.tool_name==="AskUserQuestion"){if(I=this.computeAskUserQuestionFingerprint(e.metadata.tool_input?.questions),I){let k=h.activeAskUserQuestionFingerprint===I,C=h.completedAskUserQuestionFingerprints?.has(I)??!1;if(k||C){r.info("Dropping duplicate INTERACTIVE_PROMPT \u2014 AskUserQuestion already tracked",{sessionId:a,fingerprint:I.slice(0,16),status:k?"in-flight":"completed",hookEvent:e.hook_event_name,promptId:e.prompt_id});return}}let f=e.metadata.tool_input?.questions,u=Array.isArray(f)&&f.some(k=>k?.multiSelect===!0),y=Array.isArray(f)&&(f[0]?.options?.length??0)===0;if(I&&(u||y)){h.completedAskUserQuestionFingerprints||(h.completedAskUserQuestionFingerprints=new Set),h.completedAskUserQuestionFingerprints.add(I);let k=u?"\u26A0\uFE0F This AskUserQuestion uses multi-select, which can't be answered from mobile. Please answer it on your desktop terminal.":"\u26A0\uFE0F This AskUserQuestion has no explicit options \u2014 please answer on your desktop terminal.";r.info("AUQ degraded at intercept \u2014 emitting notification, skipping walker setup",{sessionId:a,fingerprint:I.slice(0,16),hasMultiSelect:u,isZeroExplicitOption:y,questionCount:Array.isArray(f)?f.length:0}),setImmediate(()=>this.emitDegradedAUQNotification(a,k));let C=this.newE1Raise(a,!1);this.emitSuppressedPromptCarrier(a,C).catch(_=>{r.error("E1: failed to emit degraded-AUQ badge carrier",{sessionId:a,promptId:C.promptId,error:_ instanceof Error?_.message:String(_)})});return}}if(h){let f=this.newE1Raise(a,!0);e.prompt_id=f.promptId,this.clearPromptState(h),h.waitingForPromptResponse=!0,h.pendingPromptId=e.prompt_id;let u=this.nextPromptGen++;h.promptGenerationToken={promptId:e.prompt_id||"",gen:u},I&&(h.activeAskUserQuestionFingerprint=I),r.info("Interactive prompt detected - will parse options from tmux",{sessionId:a,promptId:e.prompt_id,tokenGen:u,askUserQuestionFingerprint:I?.slice(0,16)})}this.sendInteractivePromptAsync(a,e,s).catch(f=>{r.error("Failed to send interactive prompt with dynamic options",{error:f})});return}if(n===p.EventType.TOOL_USE&&this.toolActivity?.isActive()&&await this.toolActivity.observeHookTool({toolUseId:e.tool_use_id,toolName:e.metadata?.tool_name,toolInput:e.tool_input})!=="closed")return;n===p.EventType.TOOL_USE&&e.tool_use_id&&this.sessionKey&&(c=String(e.tool_use_id),o=this.getToolActivityLegacyRecorder(a),o.markLegacyInFlight(c)),r.info("Hook event encryption state",{type:n,sessionId:a,hasSessionKey:!!this.sessionKey,sessionKeyLength:this.sessionKey?.length||0});let d=this.encryptForEmit(s,e.metadata,n,a);if(!d)return;let w=d.content,l=d.metadata,v=d.isEncrypted,S=await this.appSyncClient.createEvent({sessionId:a,type:n,source:e.source,content:w,metadata:l,promptId:e.prompt_id,timestamp:(0,p.prepareEventTimestamp)({orderingKey:a}),isEncrypted:v?!0:void 0});if(o&&c&&((0,p.isPersistedEventResult)(S)?await o.finalizeLegacyOwned([c]):o.clearLegacyInFlight(c)),n===p.EventType.USER_PROMPT&&e.source===p.EventSource.DESKTOP){let h=this.activeSessions.get(a);h?.waitingForPromptResponse&&(this.promoteFingerprintAndClearPromptState(h),r.info("Clearing prompt wait state - new desktop prompt received",{sessionId:a}))}r.debug("Event sent to AppSync successfully")}catch(a){throw o&&c&&o.clearLegacyInFlight(c),r.error("Failed to process hook event:",a),a}}buildToolActivityIntegration(){return new pe({logger:r,createEvent:e=>this.appSyncClient.createEvent(e),encryptContent:(e,t)=>p.cryptoService.encryptContent(e,t),encryptMetadata:(e,t)=>p.cryptoService.encryptMetadata(e,t),getSessionKey:()=>this.sessionKey,getSessionKeyGen:()=>this.sessionKeyGen,refreshSessionKey:e=>this.refreshToolActivitySessionKey(e)})}async refreshToolActivitySessionKey(e){try{let t=await this.appSyncClient.getSession(e);if(t&&t.status===p.SessionStatus.INACTIVE){let o=new Error(`SESSION_TERMINAL: session ${e} is ${t.status}; stop retrying`);throw o.errorType="SessionTerminal",o}if(!t||t.status!==p.SessionStatus.ACTIVE||!t.isEncrypted)return null;let i=t.sessionKeyGen??null,n=t.encryptedKeys??[];if(n.length===0)return null;let s=await p.keychainManager.getSessionKey(e,n,i);return s?(r.info("[tool-activity] refreshed session key for SessionKeyStale retry (live key untouched)",{sessionId:e,sessionKeyGen:i}),{sessionKey:s,sessionKeyGen:i}):null}catch(t){if((0,p.isTerminalSessionError)(t))throw t;return r.warn("[tool-activity] session key refresh failed after SessionKeyStale (staying retryable)",{sessionId:e,error:String(t)}),null}}getToolActivityLegacyRecorder(e){let t=this.toolActivityLegacyRecorders.get(e);return t||(t=new p.ToolActivityLedger({agent:"claude",sessionId:e,logger:r}),this.toolActivityLegacyRecorders.set(e,t)),t}handleSessionStart(e,t,i="explicit"){if(this.isStopping)return Promise.resolve();let n=e.session_id,s=i==="explicit"?this.recordExplicitClaudeSessionStart(n):{ownershipGeneration:this.claudeOwnershipGeneration,sessionIntentVersion:this.getClaudeSessionIntentVersion(n)};return this.enqueueSessionLifecycle(async()=>{if(this.isStopping)return;if(i==="self-heal"){if(!this.canSelfHealClaudeSession(n,s)){r.warn("Rejected stale Claude self-heal request",{claudeSessionId:n,currentClaudeSessionId:this.currentClaudeSessionId});return}this.currentClaudeSessionId===null&&(this.currentClaudeSessionId=n,s.ownershipGeneration=++this.claudeOwnershipGeneration)}else if(!this.isClaudeStartIntentCurrent(n,s))return;let o=++this.lifecycleGeneration;await this.handleSessionStartSerialized(e,t,o,()=>this.isClaudeStartIntentCurrent(n,s))})}getClaudeSessionIntentVersion(e){return this.claudeSessionIntentVersions.get(e)??0}bumpClaudeSessionIntentVersion(e){let t=this.getClaudeSessionIntentVersion(e)+1;return this.claudeSessionIntentVersions.set(e,t),t}recordExplicitClaudeSessionStart(e){this.clearEncryptedKeyRecovery("explicit Claude SessionStart");let t=this.currentClaudeSessionId;t&&t!==e&&this.endedOrSupersededClaudeSessionIds.add(t);for(let i of this.activeSessions.values())i.claudeSessionId!==e&&this.endedOrSupersededClaudeSessionIds.add(i.claudeSessionId);return this.currentClaudeSessionId=e,this.endedOrSupersededClaudeSessionIds.delete(e),{ownershipGeneration:++this.claudeOwnershipGeneration,sessionIntentVersion:this.bumpClaudeSessionIntentVersion(e)}}recordExplicitClaudeSessionEnd(e){this.clearEncryptedKeyRecovery("explicit Claude SessionEnd",e),this.endedOrSupersededClaudeSessionIds.add(e),this.bumpClaudeSessionIntentVersion(e),this.currentClaudeSessionId===e&&(this.currentClaudeSessionId=null,this.claudeOwnershipGeneration+=1)}isClaudeStartIntentCurrent(e,t){return!this.isStopping&&this.currentClaudeSessionId===e&&this.claudeOwnershipGeneration===t.ownershipGeneration&&this.getClaudeSessionIntentVersion(e)===t.sessionIntentVersion&&!this.endedOrSupersededClaudeSessionIds.has(e)}canSelfHealClaudeSession(e,t){return this.endedOrSupersededClaudeSessionIds.has(e)||this.getClaudeSessionIntentVersion(e)!==t.sessionIntentVersion||this.claudeOwnershipGeneration!==t.ownershipGeneration||this.currentClaudeSessionId&&this.currentClaudeSessionId!==e?!1:!Array.from(this.activeSessions.values()).some(i=>i.claudeSessionId!==e)}enqueueSessionLifecycle(e){let t=this.sessionStartChain;this.sessionLifecycleQueueDepth+=1;let i=this.sessionLifecycleQueueDepth===1?e():t.catch(()=>{}).then(e),n=i.finally(()=>{this.sessionLifecycleQueueDepth-=1});return this.sessionStartChain=n.catch(()=>{}),i}async handleSessionStartSerialized(e,t,i=this.lifecycleGeneration,n=()=>!0){let s=()=>this.isLifecycleCurrent(i)&&n();if(!s())return;let o=e.session_id,c=this.appSyncClient.getCurrentUserId(),a=this.claudeToBackendSessionId.get(o),m=t||a?null:await this.sessionBindingStore.read(c,o);if(!s())return;let d=t??a??m??this.generateBackendSessionId(o),w=e.metadata?.cwd||process.cwd();this.claudeToBackendSessionId.set(o,d),r.info("Session started",{claudeSessionId:o,sessionId:d,cwd:w});let l=this.activeSessions.get(d);if(l){if(l.mobileEndWatcher?.stop(),l.mobileEndWatcher=void 0,l.orchestrationPromptWatcher?.stop(),l.orchestrationPromptWatcher=void 0,l.orchestrationDecisionWatcher?.stop(),l.orchestrationDecisionWatcher=void 0,l.orchestrationClient){try{await l.orchestrationClient.destroy()}catch(f){r.warn("[v1-bridge] OrchestrationClient.destroy() failed on same-session restart",{sessionId:d,error:String(f)})}l.orchestrationClient=void 0}this.appSyncClient.stopHeartbeat(d),this.appSyncClient.cleanupSubscription(d),this.activeSessions.get(d)===l&&this.activeSessions.delete(d)}let v=Array.from(this.activeSessions.keys()).filter(f=>f!==d);if(v.length>0){r.info(`Marking ${v.length} previous session(s) as INACTIVE`);for(let f of v){let u=this.activeSessions.get(f);if(u?.mobileEndWatcher&&(u.mobileEndWatcher.stop(),u.mobileEndWatcher=void 0),u?.orchestrationPromptWatcher&&(u.orchestrationPromptWatcher.stop(),u.orchestrationPromptWatcher=void 0),u?.orchestrationDecisionWatcher&&(u.orchestrationDecisionWatcher.stop(),u.orchestrationDecisionWatcher=void 0),u?.orchestrationClient){try{await u.orchestrationClient.destroy()}catch(y){r.warn("[v1-bridge] OrchestrationClient.destroy() failed on resume",{prevId:f,error:String(y)})}u.orchestrationClient=void 0}this.appSyncClient.stopHeartbeat(f),this.appSyncClient.cleanupSubscription(f);try{await this.appSyncClient.updateSession({sessionId:f,status:p.SessionStatus.INACTIVE}),r.info("Previous session marked INACTIVE",{prevId:f,newSessionId:d})}catch(y){r.warn("Failed to mark previous session as INACTIVE",{prevId:f,error:y})}u&&this.removePortFile(u.claudeSessionId),this.activeSessions.delete(f)}}this.writePortFile(o);let S={sessionId:d,claudeSessionId:o,userId:c,projectPath:w,cwd:w,createdAt:new Date,subscriptionActive:!1,waitingForPromptResponse:!1,metadata:e.metadata||{},transcriptPath:e.transcript_path,originatedDecisions:new Set,externallyResolvedGateIds:new Set};this.encryptionResolved=!1,S.encryptionSnapshot={sessionKey:null,sessionIsEncrypted:!1,encryptionResolved:!1},this.activeSessions.set(d,S);try{let f;try{f=await(0,p.resumeOrCreateSession)({sessionId:d,userId:S.userId,agentType:p.AgentType.CLAUDE,projectPath:w,metadata:e.metadata||{}},this.appSyncClient,r)}catch(u){if(!(0,p.isRetiredSessionCannotReactivateError)(u))throw u;if(!s()||this.activeSessions.get(d)!==S){this.activeSessions.get(d)===S&&this.activeSessions.delete(d),this.claudeToBackendSessionId.get(o)===d&&this.claudeToBackendSessionId.delete(o),this.removePortFile(o);return}let y=d;this.appSyncClient.stopHeartbeat(y),this.appSyncClient.cleanupSubscription(y),this.activeSessions.delete(y);let k=ge(c,o,y);if(await this.sessionBindingStore.write(c,o,k),!s())return;d=k,S.sessionId=d,this.activeSessions.set(d,S),this.claudeToBackendSessionId.set(o,d),r.warn("Backend rejected retired Claude generation during SessionStart; creating replacement row",{claudeSessionId:o,retiredSessionId:y,replacementSessionId:d}),f=await(0,p.resumeOrCreateSession)({sessionId:d,userId:S.userId,agentType:p.AgentType.CLAUDE,projectPath:w,metadata:e.metadata||{}},this.appSyncClient,r)}if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:f.sessionId,claudeSessionId:o,sessionState:S});return}if(this.activeSessions.get(d)!==S){r.warn("Ignoring superseded Claude session bootstrap completion",{claudeSessionId:o,requestedSessionId:d,authoritativeSessionId:f.sessionId}),await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:f.sessionId,claudeSessionId:o,sessionState:S});return}if(f.sessionId!==d){if(await this.sessionBindingStore.write(c,o,f.sessionId),!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:f.sessionId,claudeSessionId:o,sessionState:S});return}this.activeSessions.get(d)===S&&this.activeSessions.delete(d),d=f.sessionId,S.sessionId=d,this.activeSessions.set(d,S),this.claudeToBackendSessionId.set(o,d),r.info("Bound Claude session to recovered backend identity",{claudeSessionId:o,sessionId:d})}if(this.sessionKey=f.sessionKey,this.sessionKeyGen=f.sessionKeyGen,this.sessionIsEncrypted=!!f.sessionKey,this.encryptionResolved=!0,S.encryptionSnapshot={sessionKey:f.sessionKey,sessionIsEncrypted:!!f.sessionKey,encryptionResolved:!0},this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null,!s())){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:f.sessionId,claudeSessionId:o,sessionState:S});return}if(this.sessionKey){let u=this.buildToolActivityIntegration();try{if(await u.start(d,e.transcript_path),!s()){await u.stop().catch(()=>{}),await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:f.sessionId,claudeSessionId:o,sessionState:S});return}this.toolActivity=u}catch(y){r.warn("[tool-activity] consolidator start failed \u2014 keeping per-tool TOOL_USE",{sessionId:d,error:String(y)}),this.toolActivity=null,await u.stop().catch(()=>{})}}}catch(f){if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:this.authoritativeSessionIdFromError(f)??d,claudeSessionId:o,sessionState:S});return}let u=f?.authoritativeSessionId;if(typeof u=="string"&&u.length>0&&u!==d&&this.activeSessions.get(d)===S&&(this.activeSessions.delete(d),d=u,S.sessionId=d,this.activeSessions.set(d,S),this.claudeToBackendSessionId.set(o,d),r.info("Bound keyless Claude recovery to authoritative backend identity",{claudeSessionId:o,sessionId:d})),this.isSessionLimitExceeded(f)){this.displaySubscriptionLimitError(f,"session"),this.activeSessions.get(d)===S&&this.activeSessions.delete(d),this.removePortFile(o);return}if(f?.code==="ENCRYPTED_SESSION_NO_KEY"){this.sessionIsEncrypted=!0,this.sessionKey=null,this.encryptionResolved=!0,S.encryptionSnapshot={sessionKey:null,sessionIsEncrypted:!0,encryptionResolved:!0};let y=this.enterEncryptedKeyRecovery(e,d);this.appSyncClient.cleanupSubscription(d),await this.invalidateEncryptedRecoveryLiveness(y),r.error("Failed to create/resume session:",f);return}else{this.activeSessions.get(d)===S&&this.activeSessions.delete(d),r.error("Failed to create/resume session \u2014 entry removed so the next hook event re-runs bootstrap via self-heal (#638 G1):",f);return}}if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:d,claudeSessionId:o,sessionState:S});return}this.subscribeToMobileEvents(d);try{await this.drainParkedE1Orphans(o,d,s)}catch(f){r.warn("E1: parked-orphan drain threw (non-fatal; TTL drain will retry)",{sessionId:d,error:f instanceof Error?f.message:String(f)})}if(!await this.appSyncClient.startHeartbeat(d)){r.error("Initial Claude session heartbeat was not persisted",{sessionId:d});let f=this.encryptedKeyRecovery;if(f?.claudeSessionId===o&&f.backendSessionId===d){let u=this.enterEncryptedKeyRecovery(e,d);this.appSyncClient.cleanupSubscription(d),await this.invalidateEncryptedRecoveryLiveness(u);return}}if(!s()){await this.retireCancelledClaudeBootstrap({requestedSessionId:d,authoritativeSessionId:d,claudeSessionId:o,sessionState:S});return}let I=this.activeSessions.get(d);if(I){I.mobileEndWatcher=this.appSyncClient.watchForMobileEnd(d,async()=>{r.info("Mobile ended session \u2014 sending desktop quit",{sessionId:d}),this.appSyncClient.stopHeartbeat(d),this.appSyncClient.cleanupSubscription(d);try{await this.appSyncClient.updateSession({sessionId:d,status:p.SessionStatus.INACTIVE}),r.info("Marked session INACTIVE on mobile-end (before tmux terminate)",{sessionId:d})}catch(y){r.warn("Failed to mark session INACTIVE on mobile-end",{sessionId:d,error:y})}let u=process.env[X];if(!u){r.warn("No tmux session set; skipping desktop self-terminate",{sessionId:d,expectedEnv:X});return}await lt(u,at)});let f=!1;try{I.orchestrationPromptWatcher=this.subscribeToOrchestrationPrompts(d)}catch(u){f=!0,r.warn("[v1-bridge] Failed to start orchestration prompt watcher",{sessionId:d,error:String(u)})}try{I.orchestrationDecisionWatcher=await this.subscribeToApplyUserDecision(I)}catch(u){f=!0,r.warn("[v1-bridge] Failed to start applyUserDecision watcher",{sessionId:d,error:String(u)})}f&&this.emitBridgeUnavailableBanner()}this.clearEncryptedKeyRecovery("session key recovered and liveness established",o,d)}recoverRetiredBackendSession(e,t){let i=this.retiredSessionRecoveryPromises.get(e);if(i)return i;if(this.isStopping)return Promise.resolve();let n=t.claudeSessionId,s={ownershipGeneration:this.claudeOwnershipGeneration,sessionIntentVersion:this.getClaudeSessionIntentVersion(n)},o=()=>this.isClaudeStartIntentCurrent(n,s),a=this.enqueueSessionLifecycle(async()=>{if(this.isStopping||!o()||this.activeSessions.get(e)!==t||this.claudeToBackendSessionId.get(t.claudeSessionId)!==e)return;let m={session_id:t.claudeSessionId,metadata:t.metadata,transcript_path:t.transcriptPath};r.warn("Backend retired live Claude session; creating replacement row",{sessionId:e,claudeSessionId:t.claudeSessionId}),this.parkE1Orphans(t.claudeSessionId,this.snapshotOpenE1(e));let d=ge(t.userId,t.claudeSessionId,e);if(await this.sessionBindingStore.write(t.userId,t.claudeSessionId,d),!o())return;let w=++this.lifecycleGeneration;await this.handleSessionEndSerialized({session_id:t.claudeSessionId,metadata:{reason:"backend-generation-retired"}}),!(!this.isLifecycleCurrent(w)||!o())&&await this.handleSessionStartSerialized(m,d,w,o)}).finally(()=>{this.retiredSessionRecoveryPromises.get(e)===a&&this.retiredSessionRecoveryPromises.delete(e)});return this.retiredSessionRecoveryPromises.set(e,a),a}isLifecycleCurrent(e){return!this.isStopping&&this.lifecycleGeneration===e}authoritativeSessionIdFromError(e){let t=e?.authoritativeSessionId;return typeof t=="string"&&t.length>0?t:null}async retireCancelledClaudeBootstrap(e){let t=new Set([e.requestedSessionId,e.authoritativeSessionId]);for(let i of t)this.appSyncClient.stopHeartbeat(i),this.appSyncClient.cleanupSubscription(i);this.activeSessions.get(e.requestedSessionId)===e.sessionState&&this.activeSessions.delete(e.requestedSessionId),this.activeSessions.get(e.authoritativeSessionId)===e.sessionState&&this.activeSessions.delete(e.authoritativeSessionId),this.claudeToBackendSessionId.get(e.claudeSessionId)===e.requestedSessionId&&this.claudeToBackendSessionId.delete(e.claudeSessionId),this.removePortFile(e.claudeSessionId);try{await this.appSyncClient.updateSession({sessionId:e.authoritativeSessionId,status:p.SessionStatus.INACTIVE}),r.info("Cancelled Claude bootstrap row marked INACTIVE",{sessionId:e.authoritativeSessionId,claudeSessionId:e.claudeSessionId})}catch(i){r.warn("Cancelled Claude bootstrap row could not be marked INACTIVE",{sessionId:e.authoritativeSessionId,error:i instanceof Error?i.message:String(i)})}}async emitBridgeUnavailableBanner(){let e=process.env[X],t="[CodeVibe] Cross-device decision sync unavailable \u2014 mobile responses will be sent as free-text";if(r.warn(t),!!e)try{await le(`tmux display-message -t "${e}" -- ${JSON.stringify(t)}`)}catch(i){r.warn("[v1-bridge] tmux display-message for bridge-unavailable banner failed",{tmuxSession:e,error:String(i)})}}handleSessionEnd(e){return this.isStopping?Promise.resolve():(this.recordExplicitClaudeSessionEnd(e.session_id),this.enqueueSessionLifecycle(()=>this.handleSessionEndSerialized(e)))}async handleSessionEndSerialized(e){let t=e.session_id,i=this.claudeToBackendSessionId.get(t)||this.generateBackendSessionId(t);r.info("Session ended",{claudeSessionId:t,sessionId:i,reason:e.metadata?.reason});let n=this.activeSessions.get(i);if(n?.mobileEndWatcher&&(n.mobileEndWatcher.stop(),n.mobileEndWatcher=void 0),n?.orchestrationPromptWatcher&&(n.orchestrationPromptWatcher.stop(),n.orchestrationPromptWatcher=void 0),n?.orchestrationDecisionWatcher&&(n.orchestrationDecisionWatcher.stop(),n.orchestrationDecisionWatcher=void 0),n?.orchestrationClient){try{await n.orchestrationClient.destroy()}catch(o){r.warn("[v1-bridge] OrchestrationClient.destroy() failed",{sessionId:i,error:String(o)})}n.orchestrationClient=void 0}this.toolActivity&&this.toolActivity.sessionId===i&&(await this.toolActivity.stop(),this.toolActivity=null),this.activeSessions.delete(i),this.claudeToBackendSessionId.delete(t),this.removePortFile(t),n?.waitingForPromptResponse&&(r.info("Clearing prompt wait state - session ending",{sessionId:i}),this.clearPromptState(n));let s=this.toolActivityLegacyRecorders.get(i);if(s&&s.disposeLegacyCoordination(),this.toolActivityLegacyRecorders.delete(i),this.appSyncClient.stopHeartbeat(i),this.appSyncClient.cleanupSubscription(i),n)try{await this.appSyncClient.updateSession({sessionId:i,status:p.SessionStatus.INACTIVE}),r.info("Session marked as INACTIVE in AppSync",{sessionId:i})}catch(o){r.warn("Failed to update session in AppSync:",o)}else r.warn("Cannot update session - session state not found",{sessionId:i});r.debug("Session cleanup completed",{sessionId:i})}subscribeToMobileEvents(e){r.info("Subscribing to mobile events",{sessionId:e});let t=this.activeSessions.get(e);if(!t){r.error("Session not found",{sessionId:e});return}this.appSyncClient.subscribeToEvents(e,async i=>{r.info("Received mobile event",{eventId:i.eventId,type:i.type,sessionId:i.sessionId,isEncrypted:i.isEncrypted});let n=t.pendingPanePromptIdentity,s=i.content||"";if(i.isEncrypted&&this.sessionKey)try{s=p.cryptoService.decryptContent(i.content,this.sessionKey),r.debug("Event decrypted successfully",{eventId:i.eventId})}catch(c){r.error("Failed to decrypt event:",{eventId:i.eventId,error:c}),s=i.content}let o={...i,content:s};try{await this.appSyncClient.updateEventStatus({eventId:i.eventId,sessionId:i.sessionId,timestamp:i.timestamp,deliveryStatus:p.DeliveryStatus.DELIVERED}),r.info("Event marked as DELIVERED",{eventId:i.eventId})}catch(c){r.warn("Failed to mark event as DELIVERED",{eventId:i.eventId,error:c})}if(i.type===p.EventType.USER_PROMPT){let c=this.activeSessions.get(e);if(c?.pendingOrchestrationGate&&await this.handleOrchestrationPromptResponse(c,s,i))return;if(c?.waitingForPromptResponse){let a=s.trim(),m=c.pendingSubmitMap?Object.keys(c.pendingSubmitMap).length:3,d=this.parseInteractivePromptInput(a,m);if(r.info("Parsed interactive prompt input",{sessionId:e,content:a,parsed:d,hasSubmitMap:!!c.pendingSubmitMap}),d.action==="select_option"){let w=c.pendingSubmitMap?.[d.option]||d.option;if(r.info("User selected option",{option:d.option,terminalInput:w}),await this.answerVerifiedInteractivePrompt(c,n,w)){await this.markEventExecuted(i),c.waitingForPromptResponse=!1,c.pendingPromptId=void 0,c.pendingSubmitMap=void 0,c.pendingPanePromptIdentity=void 0;let v=this.encryptForEmit(`Selected option ${d.option}`,{promptAnswered:!0},p.EventType.NOTIFICATION,e);v&&await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:v.content,metadata:v.metadata,isEncrypted:v.isEncrypted,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e})})}else await this.sendPromptError(e,"Failed to select option")}else if(d.action==="option_with_followup"){let w=c.pendingSubmitMap?.[d.option]||d.option;r.info("User selected option with follow-up",{option:d.option,terminalInput:w,followUpText:d.followUpText});let l=await this.answerVerifiedInteractivePrompt(c,n,w);if(c.waitingForPromptResponse=!1,c.pendingPromptId=void 0,c.pendingSubmitMap=void 0,c.pendingPanePromptIdentity=void 0,l){let v=this.encryptForEmit(`Selected option ${d.option}`,{promptAnswered:!0},p.EventType.NOTIFICATION,e);if(v&&await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:v.content,metadata:v.metadata,isEncrypted:v.isEncrypted,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e})}),d.followUpText){await new Promise(h=>setTimeout(h,1e3));let S={...i,content:d.followUpText};await this.executeMobilePrompt(e,S)}await this.markEventExecuted(i)}else await this.sendPromptError(e,"Failed to select option")}else{if(r.info("Rejecting active prompt before forwarding free-form mobile input",{responsePreview:a.slice(0,80),attachmentCount:o.attachments?.length??0}),!await this.sendVerifiedPromptKey(c,n,"Escape")){await this.sendPromptError(e,"Failed to dismiss the active prompt; your message was not sent");return}c.waitingForPromptResponse=!1,c.pendingPromptId=void 0,c.pendingSubmitMap=void 0,c.pendingPanePromptIdentity=void 0,await new Promise(l=>setTimeout(l,200)),await this.executeMobilePrompt(e,o)}}else await this.executeMobilePrompt(e,o)}},i=>{r.error("Subscription error",{sessionId:e,error:i})},{onSessionRetired:()=>this.recoverRetiredBackendSession(e,t)}),t.subscriptionActive=!0,r.info("Subscription active",{sessionId:e})}async dispatchMobileEvent(e,t){r.info("Received mobile event",{eventId:t.eventId,type:t.type,sessionId:t.sessionId,isEncrypted:t.isEncrypted});let i,n,s=!1,o,c,a;if(t.type===p.EventType.USER_PROMPT||t.type===p.EventType.PROMPT_RESPONSE)if(n=this.activeSessions.get(e),!n)i="no-session";else if(n.processedEventIds?.has(t.eventId))i="skip-dedup";else if(n.inFlightEventIds?.has(t.eventId))i="drop-event-redeliver";else if(n.waitingForPromptResponse){let l=n.promptGenerationToken;if(!l)i="regular";else if(t.type===p.EventType.USER_PROMPT&&n.hasReceivedPromptResponse&&(!t.promptId||t.promptId.length===0))i="drop-stale-answer";else if(t.promptId&&t.promptId.length>0&&l.promptId.length>0&&t.promptId!==l.promptId)i="drop-stale-answer";else{let v=l.promptId.length>0?l.promptId:`__prompt_gen_${l.gen}`;n.inFlightPromptIds?.has(v)?i="drop-in-flight":(n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightEventIds||(n.inFlightEventIds=new Set),n.inFlightPromptIds.add(v),n.inFlightEventIds.add(t.eventId),s=!0,c=v,a=t.eventId,o={promptId:l.promptId,gen:l.gen},i="walker")}}else i="regular";else i="not-user-prompt";let d=t.content||"";if(t.isEncrypted&&this.sessionKey)try{d=p.cryptoService.decryptContent(t.content,this.sessionKey),r.debug("Event decrypted successfully",{eventId:t.eventId})}catch(l){r.error("Failed to decrypt event:",{eventId:t.eventId,error:l}),d=t.content}let w={...t,content:d};if(i!=="skip-dedup")try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:p.DeliveryStatus.DELIVERED}),r.info("Event marked as DELIVERED",{eventId:t.eventId})}catch(l){r.warn("Failed to mark event as DELIVERED",{eventId:t.eventId,error:l})}if(i==="skip-dedup"){r.info("[walker] Subscription-level dedup hit (already processed) \u2014 skipping",{sessionId:e,eventId:t.eventId});return}if(i==="drop-stale-answer"){r.info("[walker] Stale answer dropped \u2014 event.promptId does not match current pending promptId",{sessionId:e,eventId:t.eventId,eventPromptId:t.promptId,currentPromptId:n?.promptGenerationToken?.promptId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("[walker] markEventExecuted threw on stale-answer drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}if(i==="drop-in-flight"){r.warn("[walker] Subscription-level in-flight guard \u2014 dropping duplicate USER_PROMPT (different eventId, same prompt)",{sessionId:e,eventId:t.eventId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("[walker] markEventExecuted threw on subscription-level duplicate drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}if(i==="drop-event-redeliver"){r.info("[walker] Subscription-level event-level redelivery \u2014 silent skip (original still in flight)",{sessionId:e,eventId:t.eventId});return}if(i==="walker"){t.type===p.EventType.PROMPT_RESPONSE&&n&&(n.hasReceivedPromptResponse=!0),await this.handleMobilePromptResponse(e,t,d,n,s,o,c,a);return}if(i==="regular"){if(t.type===p.EventType.PROMPT_RESPONSE){r.warn("Received PROMPT_RESPONSE with no active walker \u2014 dropping",{sessionId:e,eventId:t.eventId,promptId:t.promptId}),n&&(n.processedEventIds||(n.processedEventIds=new Set),n.processedEventIds.add(t.eventId));try{await this.markEventExecuted(t)}catch(l){r.warn("markEventExecuted threw on PROMPT_RESPONSE orphan drop \u2014 relying on processedEventIds Set",{sessionId:e,eventId:t.eventId,error:String(l)})}return}await this.executeMobilePrompt(e,w);return}if(i==="no-session"){r.warn("Received mobile prompt input for unknown session \u2014 ignoring",{sessionId:e,eventId:t.eventId,type:t.type});return}}async handleMobilePromptResponse(e,t,i,n,s=!1,o,c,a){let m=o??n.promptGenerationToken,d=c,w=a;if(!s&&m){let l=m.promptId.length>0?m.promptId:`__prompt_gen_${m.gen}`;if(n.inFlightPromptIds?.has(l)){r.warn("[walker] Duplicate mobile USER_PROMPT for same prompt \u2014 dropping",{sessionId:e,eventId:t.eventId,lockKey:l}),await this.markEventExecutedIdempotent(n,t);return}n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightEventIds||(n.inFlightEventIds=new Set),n.inFlightPromptIds.add(l),n.inFlightEventIds.add(t.eventId),d=l,w=t.eventId}try{if(!s&&n.processedEventIds?.has(t.eventId)){r.info("[walker] Redelivered event already processed \u2014 skipping",{sessionId:e,eventId:t.eventId});return}let l=i.trim(),v=n.pendingPromptId,S=n.pendingSubmitMap,h=n.pendingPanePromptIdentity,I=S,f=I?Object.keys(I).length:3,u=this.parseInteractivePromptInput(l,f);r.info("Parsed interactive prompt input",{sessionId:e,content:l,parsed:u,hasSubmitMap:!!I});let y=()=>{let k=n.promptGenerationToken,C=k?.gen,_=m?.gen;return C!==_?(r.warn("[walker] Token mismatch \u2014 external cleanup or new prompt during in-flight handler \u2014 aborting",{sessionId:e,eventId:t.eventId,entryToken:m,currentToken:k}),!0):!1};if(y()){await this.markEventExecutedIdempotent(n,t);return}{let k=n.pendingQuestionsQueue!==void 0,C=l.trim(),_=C.match(/^(\d+)$/);if(k&&n.pendingCurrentQuestion&&_){let E=n.pendingCurrentQuestion.options?.length??0,A=_[1],T=parseInt(A,10),F=!Number.isFinite(T)||T<1||T>E,P=String(T)!==A;if(F||P){let N=this.getWalkerPosition(n);if(r.info("AUQ walker \u2014 bare out-of-range or non-canonical option; routing per dispatch matrix",{sessionId:e,option:A,optionNum:T,realOptionCount:E,isOutOfRange:F,isNonCanonical:P,walkerPosition:N,parsedAction:u.action}),await this.markEventExecutedIdempotent(n,t),y())return;if(N==="on_synth"){let x=await this.answerVerifiedInteractivePrompt(n,h,"2",{pressEnter:!1});if(y())return;if(!x){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(O){r.warn("emitUserChoice on on-SYNTH Cancel failed",{sessionId:e,error:O instanceof Error?O.message:String(O)})}return}if(await new Promise(O=>setTimeout(O,1500)),y())return;let M=await this.sendFreeFormWhenNoChooser(e,n,C);if(y())return;M||r.warn("on-SYNTH Cancel followup text-send failed; AUQ cancelled, no new prompt",{sessionId:e});try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(O){r.warn("emitUserChoice on on-SYNTH Cancel success failed",{sessionId:e,error:O instanceof Error?O.message:String(O)})}if(y())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,h,C,y);return}}}if(u.action==="select_option"){let k=I?.[u.option]||u.option,C=n.pendingQuestionsQueue!==void 0;r.info("User selected option",{option:u.option,terminalInput:k,isV2AskUserQuestion:C});let _=await this.answerVerifiedInteractivePrompt(n,h,k,{pressEnter:!C});if(y()){await this.markEventExecutedIdempotent(n,t);return}if(_){if(await this.markEventExecutedIdempotent(n,t),y())return;if(!v){r.warn("emitAnswerAck called without promptId \u2014 clearing state + skipping ack",{sessionId:e,source:"select_option",eventId:t.eventId}),this.promoteFingerprintAndClearPromptState(n);return}let E=(n.pendingQuestionsQueue?.length??0)===0;try{if(C){let V=parseInt(u.option,10)-1,H=n.pendingCurrentQuestion?.options?.[V],W=typeof H=="string"?H:H&&typeof H=="object"?H.label:`option ${u.option}`,Ie=n.pendingCurrentQuestion?._isSubmit===!0,Ee=W.toLowerCase(),ie;Ie&&Ee==="cancel"?ie="\u2192 Cancel \u2014 AskUserQuestion cancelled, no answers submitted":Ie&&Ee.startsWith("submit")?ie="\u2192 Submit answers \u2014 AskUserQuestion completed":ie=`\u2192 ${W}`,await this.emitUserChoice(e,ie)}else await this.emitAnswerAck(e,`Selected option ${u.option}`,{promptId:v,questionIndex:0,isTerminal:E})}catch(V){r.warn("[walker] user-choice/ack emit failed \u2014 continuing to STEP 7/8",{sessionId:e,promptId:v,isV2AskUserQuestion:C,error:V instanceof Error?V.message:String(V)})}if(y())return;let A=n.pendingQuestionsQueue?.shift();if(A&&(n.pendingCurrentQuestion=A,n.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(A)),!A){n.activeAskUserQuestionFingerprint&&(n.completedAskUserQuestionFingerprints||(n.completedAskUserQuestionFingerprints=new Set),n.completedAskUserQuestionFingerprints.add(n.activeAskUserQuestionFingerprint),r.info("AskUserQuestion V2 walker complete \u2014 fingerprint marked closed",{sessionId:e,fingerprint:n.activeAskUserQuestionFingerprint.slice(0,16)})),this.clearPromptState(n);return}let T=this.newE1Raise(e,!0),F=T.promptId;if(!F){this.promoteFingerprintAndClearPromptState(n),r.warn("Q[next] emit aborted: synthesized promptId was empty; promoted fingerprint + cleared prompt state",{sessionId:e,eventId:t.eventId});return}let P=n.pendingSynthTail??[],N=this.buildQuestionWireData(A,P),x=A.question,M={tool_name:"AskUserQuestion",tool_input:{questions:[A]},options:N.options,submitMap:N.submitMap,instructions:N.instructions},O=n.encryptionSnapshot,$=(0,p.encryptForEmit)(O?.sessionKey??null,O?.sessionIsEncrypted??!1,x,M,O?O.encryptionResolved:!1);if(!$){r.error("No session key for ENCRYPTED session \u2014 dropping Q[next] prompt (fail-closed, #638)",{sessionId:e,promptId:F}),this.promoteFingerprintAndClearPromptState(n);return}let z=$.content,Y=$.metadata,q=$.isEncrypted,U=this.nextPromptGen++,b={promptId:F,gen:U};n.pendingPromptId=F,n.pendingSubmitMap=N.submitMap,n.promptGenerationToken=b;let B=b,ne=this.activeSessions.get(e)?.promptGenerationToken;if(!ne||ne.gen!==B.gen||ne.promptId!==B.promptId){r.warn("Q[next] emit aborted: token replaced before await dispatch",{sessionId:e,tokenAtAwait:B,currentToken:ne});return}let Se=F.length>0?F:`__prompt_gen_${b.gen}`;n.inFlightPromptIds||(n.inFlightPromptIds=new Set),n.inFlightPromptIds.add(Se);try{this.stashE1RaiseContent(F,x,M);try{await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.INTERACTIVE_PROMPT,source:p.EventSource.DESKTOP,content:z,metadata:Y,...(0,p.raiseFieldsFromRecord)(T),timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:q?!0:void 0}),r.info("Q[next] emit succeeded",{sessionId:e,promptId:F,remaining:n.pendingQuestionsQueue?.length??0})}catch(V){let H=this.activeSessions.get(e),W=H?.promptGenerationToken;W&&W.gen===B.gen&&W.promptId===B.promptId?(this.promoteFingerprintAndClearPromptState(H),r.warn("Q[next] emit failed; promoted fingerprint + cleared prompt state. User must answer remaining questions on desktop terminal.",{sessionId:e,promptId:F,error:V instanceof Error?V.message:String(V)})):r.warn("Q[next] emit failed but a NEW prompt replaced our token during await; not clearing state (would wipe new prompt). Q[next..QN] of the original AskUserQuestion are lost; new prompt continues normally.",{sessionId:e,tokenAtAwait:B,currentToken:W,error:V instanceof Error?V.message:String(V)})}}finally{n.inFlightPromptIds.delete(Se)}}else try{await this.sendPromptError(e,"Failed to select option")}catch(E){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(E)})}finally{await this.markEventExecutedIdempotent(n,t)}}else if(u.action==="option_with_followup"){if(n.pendingQuestionsQueue!==void 0){let E=this.getWalkerPosition(n),A=n.pendingCurrentQuestion?.options?.length??0,T=u.option?parseInt(u.option,10):NaN,F=Number.isFinite(T)&&T>A,P=u.followUpText??"";if(r.info("AUQ walker \u2014 option_with_followup dispatch",{sessionId:e,option:u.option,optionNum:T,explicitCount:A,isSynthDigit:F,walkerPosition:E,followUpTextLen:P.length}),await this.markEventExecutedIdempotent(n,t),y())return;if(F){if(E==="on_synth"){let U=await this.answerVerifiedInteractivePrompt(n,h,"2",{pressEnter:!1});if(y())return;if(!U){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(b){r.warn("emitUserChoice on synth-digit on-SYNTH Cancel failed",{sessionId:e,error:b instanceof Error?b.message:String(b)})}return}if(await new Promise(b=>setTimeout(b,1500)),y())return;if(P){let b=await this.sendFreeFormWhenNoChooser(e,n,P);if(y())return;b||r.warn("on-SYNTH Cancel followup text-send failed",{sessionId:e})}try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(b){r.warn("emitUserChoice on synth-digit on-SYNTH success failed",{sessionId:e,error:b instanceof Error?b.message:String(b)})}if(y())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,h,P,y);return}let N=I?.[u.option]||u.option,x=T-1,M=n.pendingCurrentQuestion?.options?.[x],O=typeof M=="string"?M:M&&typeof M=="object"?M.label:`option ${u.option}`;if(E==="q1"){let U=await this.answerVerifiedInteractivePrompt(n,h,N,{pressEnter:!1});if(y())return;if(!U){try{await this.sendPromptError(e,"Failed to select option. Your reply (including the follow-up text) was not sent. Please retry.")}catch(b){r.warn("sendPromptError on q1 option_with_followup failed",{sessionId:e,error:b instanceof Error?b.message:String(b)})}return}if(await new Promise(b=>setTimeout(b,1500)),y())return;if(P){let b=await this.sendFreeFormWhenNoChooser(e,n,P);if(y())return;b||r.warn("q1 option_with_followup followup text-send failed",{sessionId:e})}try{await this.emitUserChoice(e,P?`\u2192 ${O} + sending your reply as a new prompt`:`\u2192 ${O}`)}catch(b){r.warn("emitUserChoice on q1 option_with_followup success failed",{sessionId:e,error:b instanceof Error?b.message:String(b)})}if(y())return;this.promoteFingerprintAndClearPromptState(n);return}if(E==="nq_mid"){let U=await this.answerVerifiedInteractivePrompt(n,h,N,{pressEnter:!1});if(y())return;if(!U){try{await this.sendPromptError(e,"Failed to commit Q[i]. Your reply was not sent. Please retry.")}catch(B){r.warn("sendPromptError on nq_mid option_with_followup failed",{sessionId:e,error:B instanceof Error?B.message:String(B)})}return}if(await new Promise(B=>setTimeout(B,200)),y())return;let b=n.pendingQuestionsQueue?.shift();b?(n.pendingCurrentQuestion=b,n.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(b)):r.warn("nq_mid option_with_followup: queue.shift returned undefined unexpectedly",{sessionId:e}),await this.handleMobileReplyAsDismissAndPrompt(e,n,n.pendingPanePromptIdentity,P,y);return}if(E==="nq_last"){await this.handleOptionWithFollowupOnLastQ(e,n,h,N,O,P,y);return}let $=await this.answerVerifiedInteractivePrompt(n,h,N,{pressEnter:!1});if(y())return;if(!$){try{await this.sendPromptError(e,"Failed to send SYNTH keypress. Your reply was not sent. Please retry.")}catch(U){r.warn("sendPromptError on on-SYNTH option_with_followup failed",{sessionId:e,error:U instanceof Error?U.message:String(U)})}return}if(await new Promise(U=>setTimeout(U,1500)),y())return;if(P){let U=await this.sendFreeFormWhenNoChooser(e,n,P);if(y())return;U||r.warn("on-SYNTH option_with_followup text-send failed",{sessionId:e})}let z=n.pendingCurrentQuestion?._isSubmit===!0,Y=O.toLowerCase(),q;z&&Y==="cancel"?q=P?"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt":"\u2192 Cancel \u2014 AskUserQuestion cancelled, no answers submitted":z&&Y.startsWith("submit")?q=P?"\u2192 Submit answers \u2014 AskUserQuestion completed, sending your reply as a new prompt":"\u2192 Submit answers \u2014 AskUserQuestion completed":q=P?`\u2192 ${O} + sending your reply as a new prompt`:`\u2192 ${O}`;try{await this.emitUserChoice(e,q)}catch(U){r.warn("emitUserChoice on on-SYNTH option_with_followup success failed",{sessionId:e,error:U instanceof Error?U.message:String(U)})}if(y())return;this.promoteFingerprintAndClearPromptState(n);return}let C=I?.[u.option]||u.option;r.info("User selected option with follow-up",{option:u.option,terminalInput:C,followUpText:u.followUpText});let _=await this.answerVerifiedInteractivePrompt(n,h,C);if(y()){await this.markEventExecutedIdempotent(n,t);return}if(_){if(await this.markEventExecutedIdempotent(n,t),y())return;if(!v){r.warn("emitAnswerAck called without promptId \u2014 clearing state + skipping ack",{sessionId:e,source:"option_with_followup",eventId:t.eventId}),this.promoteFingerprintAndClearPromptState(n);return}try{await this.emitAnswerAck(e,`Selected option ${u.option}`,{promptId:v,questionIndex:0,isTerminal:!0})}catch(E){r.warn("[walker] emitAnswerAck (option_with_followup) failed \u2014 continuing to clearPromptState + executeMobilePrompt",{sessionId:e,promptId:v,error:E instanceof Error?E.message:String(E)})}if(y())return;if(this.promoteFingerprintAndClearPromptState(n),u.followUpText){await new Promise(A=>setTimeout(A,1e3));let E={...t,content:u.followUpText};await this.executeMobilePrompt(e,E)}}else try{await this.sendPromptError(e,"Failed to select option. Your reply (including the follow-up text) was not sent. Please retry.")}catch(E){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(E)})}finally{await this.markEventExecutedIdempotent(n,t)}}else{if(n.pendingQuestionsQueue!==void 0){let _=this.getWalkerPosition(n);if(r.info("AUQ walker \u2014 send_as_response dispatch",{sessionId:e,walkerPosition:_,contentPreview:l.slice(0,80)}),await this.markEventExecutedIdempotent(n,t),y())return;if(_==="on_synth"){let E=await this.answerVerifiedInteractivePrompt(n,h,"2",{pressEnter:!1});if(y())return;if(!E){try{await this.emitUserChoice(e,"AskUserQuestion cancel keypress failed (tmux unavailable); your reply was not sent")}catch(T){r.warn("emitUserChoice on send_as_response on-SYNTH Cancel failed",{sessionId:e,error:T instanceof Error?T.message:String(T)})}return}if(await new Promise(T=>setTimeout(T,1500)),y())return;let A=await this.sendFreeFormWhenNoChooser(e,n,l);if(y())return;A||r.warn("send_as_response on-SYNTH text-send failed",{sessionId:e});try{await this.emitUserChoice(e,"\u2192 Cancel \u2014 AskUserQuestion cancelled, sending your reply as a new prompt")}catch(T){r.warn("emitUserChoice on send_as_response on-SYNTH success failed",{sessionId:e,error:T instanceof Error?T.message:String(T)})}if(y())return;this.promoteFingerprintAndClearPromptState(n);return}await this.handleMobileReplyAsDismissAndPrompt(e,n,h,l,y);return}r.info("Rejecting legacy prompt before forwarding free-form mobile input",{responsePreview:l.slice(0,80),attachmentCount:t.attachments?.length??0});let C=await this.sendVerifiedPromptKey(n,h,"Escape");if(y()){await this.markEventExecutedIdempotent(n,t);return}if(C)this.promoteFingerprintAndClearPromptState(n),await new Promise(_=>setTimeout(_,200)),await this.executeMobilePrompt(e,{...t,content:l});else try{await this.sendPromptError(e,"Failed to dismiss the active prompt; your message was not sent")}catch(_){r.warn("[walker] sendPromptError threw \u2014 relying on idempotent mark in finally",{sessionId:e,eventId:t.eventId,error:String(_)})}finally{await this.markEventExecutedIdempotent(n,t)}}}finally{d&&n.inFlightPromptIds&&n.inFlightPromptIds.delete(d),w&&n.inFlightEventIds&&n.inFlightEventIds.delete(w)}}async sendInteractivePromptAsync(e,t,i){let n=this.activeSessions.get(e),s=n?.promptGenerationToken?{...n.promptGenerationToken}:void 0,o=(0,p.prepareEventTimestamp)({orderingKey:e});await new Promise(E=>setTimeout(E,500));let c=process.env.CODEVIBE_TMUX_SESSION,a={...t.metadata||{}},m,d=t.metadata?.tool_name,w=t.metadata?.tool_input,l=d==="AskUserQuestion"&&Array.isArray(w?.questions)?w.questions:[];if(l.length>0&&Array.isArray(l[0]?.options)&&l[0].options.length>0){let E=l[0];m=this.buildAskUserQuestionPaneIdentity(E);let A=[];if(c)try{let{exec:x}=await import("child_process"),M=$=>new Promise((z,Y)=>{x($,{timeout:5e3},(q,U)=>{q?Y(q):z({stdout:U||""})})}),{stdout:O}=await M(`tmux capture-pane -p -e -S -30 -t '${c}'`);A=this.parseAskUserQuestionSynthTail(O),r.info("AskUserQuestion synth-tail parsed from tmux",{tailCount:A.length,tail:A.map($=>$.label)})}catch(x){r.warn("Failed to capture tmux for AskUserQuestion synth-tail; emitting without synth tail",{error:x instanceof Error?x.message:String(x)})}else r.info("No tmux session \u2014 AskUserQuestion synth tail will be empty");let T=this.activeSessions.get(e);if(T){let x=T.promptGenerationToken;s&&x?.gen===s.gen?T.pendingSynthTail=A:r.warn("AskUserQuestion synth-tail: stale async \u2014 token gen mismatch, skipping pendingSynthTail write",{tokenAtEmit:s,currentToken:x,sessionId:e})}let F=this.buildQuestionWireData(E,A);a.options=JSON.parse(JSON.stringify(F.options)),a.submitMap=JSON.parse(JSON.stringify(F.submitMap)),a.instructions=F.instructions,a.tool_name="AskUserQuestion",a.tool_input={questions:[E]},i=E.question;let P=typeof t.prompt_id=="string"&&t.prompt_id.length>0,N=mt(l);if(P){let x=this.activeSessions.get(e);if(x){let M=x.promptGenerationToken;s&&M?.gen===s.gen?(x.pendingQuestionsQueue=N.remainingQueue,x.pendingCurrentQuestion=E,x.pendingPanePromptIdentity=m,x.hasReviewScreen=N.hasReviewScreen):r.warn("AskUserQuestion: stale async \u2014 token gen mismatch, skipping walker-state write",{tokenAtEmit:s,currentToken:M,sessionId:e})}}else r.warn("AskUserQuestion: empty prompt_id, degrading to single-Q legacy emit",{questionCount:l.length});r.info("AskUserQuestion: emitting Q1 only (Q2..QN queued)",{questionCount:N.questionCount,hasReviewScreen:N.hasReviewScreen,queuedRemaining:P?N.remainingQueue.length:0,optionCountFirst:F.options.length,questionPreview:E.question.slice(0,80)})}else if(c)try{let{exec:E}=await import("child_process"),A=N=>new Promise((x,M)=>{E(N,{timeout:5e3},(O,$)=>{O?M(O):x({stdout:$||""})})}),{stdout:T}=await A(`tmux capture-pane -p -e -S -30 -t '${c}'`),F=T.split(`
4
+ `);r.info("tmux capture result",{tmuxSession:c,totalLines:F.length,lastLines:F.slice(-15).map(N=>N.replace(/\x1B[^m]*m/g,"").trim()).filter(Boolean)});let P=(0,p.parseInteractivePrompt)(T);if(P&&P.options.length>0)a.options=P.options,a.submitMap=P.submitMap,a.instructions=this.buildPromptInstructions(P),m=this.buildParsedPanePromptIdentity(P),r.info("Parsed dynamic options from tmux",{optionCount:P.options.length,kind:P.kind,options:P.options});else{this.suppressPromptOptionsUnavailable(e,s,"tmux parse returned no options");return}}catch(E){this.suppressPromptOptionsUnavailable(e,s,`tmux capture failed: ${E instanceof Error?E.message:String(E)}`);return}else{this.suppressPromptOptionsUnavailable(e,s,"no tmux session");return}let v=this.activeSessions.get(e),S=!s||v?.promptGenerationToken?.gen===s.gen;v&&a.submitMap&&S&&(v.pendingSubmitMap=a.submitMap,v.pendingPanePromptIdentity=m);let h=this.encryptForEmit(i,a,p.EventType.INTERACTIVE_PROMPT,e);if(!h)return;let I=h.content,f=h.metadata,u=h.isEncrypted,k=this.activeSessions.get(e)?.promptGenerationToken;if(s&&k?.gen!==s.gen){r.warn("Interactive prompt emit: stale token \u2014 newer INTERACTIVE_PROMPT replaced ours; skipping AppSync emit",{sessionId:e,tokenAtEmit:s,currentToken:k});return}let C=t.prompt_id?this.e1PromptRaises.get(t.prompt_id):void 0,_=C?(0,p.raiseFieldsFromRecord)(C.record):{};C&&this.stashE1RaiseContent(C.record.promptId,i,a),await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.INTERACTIVE_PROMPT,source:t.source,content:I,metadata:f,promptId:t.prompt_id,..._,timestamp:o,isEncrypted:u?!0:void 0}),r.info("Interactive prompt sent to AppSync with dynamic options",{sessionId:e})}buildQuestionWireData(e,t=[]){let i=(e.options||[]).map((a,m)=>{let d=typeof a=="string",w=d?a:a.label||"",l=d?"":a.description||"",v=d?"":a.preview||"",S={number:String(m+1),text:w};return l&&(S.description=l),v&&(S.preview=v),S});if(!e._isSubmit)for(let a of t)i.push({number:String(i.length+1),text:a.label});let n=Object.fromEntries(i.map(a=>[a.number,a.number])),s=(e.options||[]).length,o=t.findIndex(a=>a.label==="Type something"),c;if(e._isSubmit)c="Reply with 1 to submit your answers or 2 to cancel.";else if(e.multiSelect)c=`Reply with comma-separated numbers (e.g., 1,3) for "${e.header||e.question}"`;else if(o>=0){let a=String(s+o+1);c=`Reply with the number of your choice. For option ${a} (Type something), reply "${a}, your answer".`}else c="Reply with the number of your choice.";return{options:i,submitMap:n,instructions:c}}parseAskUserQuestionSynthTail(e){let t=(0,p.normalizeSnapshot)(e);if(!t)return[];let i=t.split(`
5
+ `).slice(-14),n=[],s=new Set,o=/^\s*(?:[›❯▸▶➜➤]\s*)?(?:\d+\.\s+)?(Type something|Chat about this)\.?\s*$/i;for(let c of i){let a=c.match(o);if(!a)continue;let d=a[1].toLowerCase()==="type something"?"Type something":"Chat about this";s.has(d)||(n.push({label:d}),s.add(d))}return n}suppressPromptOptionsUnavailable(e,t,i){r.warn("Interactive prompt: real options unavailable \u2014 suppressing mobile prompt (no fabricated options)",{sessionId:e,reason:i});let n=t?.promptId,s=n?this.e1PromptRaises.get(n):void 0;s&&(s.record.mobileActionable=!1,this.emitSuppressedPromptCarrier(e,s.record).catch(c=>{r.error("E1: failed to emit suppressed-prompt badge carrier",{sessionId:e,promptId:s.record.promptId,error:c instanceof Error?c.message:String(c)})}));let o=this.activeSessions.get(e);o&&t&&o.promptGenerationToken?.gen===t.gen&&this.clearPromptState(o)}async emitSuppressedPromptCarrier(e,t){let i="\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.",n=this.encryptForEmit(i,{e1SuppressedPrompt:!0},p.EventType.NOTIFICATION,e);return n?(await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:n.content,metadata:n.metadata,...(0,p.raiseFieldsFromRecord)(t),notificationText:i,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:n.isEncrypted?!0:void 0}),r.info("E1: emitted suppressed-prompt badge carrier (mobileActionable=false)",{sessionId:e,promptId:t.promptId}),!0):!1}buildPromptInstructions(e){return`Reply with ${e.options.map(i=>i.number).join(", ")}. Append a message to provide alternative instructions.`}parseInteractivePromptInput(e,t=3){return je(e,t)}async markEventExecuted(e){try{await this.appSyncClient.updateEventStatus({eventId:e.eventId,sessionId:e.sessionId,timestamp:e.timestamp,deliveryStatus:p.DeliveryStatus.EXECUTED}),r.info("Event marked as EXECUTED",{eventId:e.eventId})}catch(t){r.warn("Failed to mark event as EXECUTED",{eventId:e.eventId,error:t})}}async sendPromptError(e,t){let i={error:!0},n=this.encryptForEmit(t,i,p.EventType.NOTIFICATION,e);if(!n)return;let s=n.content,o=n.metadata,c=n.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:s,metadata:o,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:c?!0:void 0})}async emitUserChoice(e,t){let i=this.encryptForEmit(t,{source:"codevibe_v2_user_choice"},p.EventType.ASSISTANT_RESPONSE,e);if(!i)return;let n=i.content,s=i.metadata,o=i.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.ASSISTANT_RESPONSE,source:p.EventSource.DESKTOP,content:n,metadata:s,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:o?!0:void 0})}async emitAnswerAck(e,t,i){let n={promptAnswered:!0,...i},s=this.encryptForEmit(t,n,p.EventType.NOTIFICATION,e);if(!s)return;let o=s.content,c=s.metadata,a=s.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:o,metadata:c,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:a?!0:void 0})}promoteFingerprintAndClearPromptState(e){e.activeAskUserQuestionFingerprint&&(e.completedAskUserQuestionFingerprints||(e.completedAskUserQuestionFingerprints=new Set),e.completedAskUserQuestionFingerprints.add(e.activeAskUserQuestionFingerprint)),this.clearPromptState(e)}normalizePromptIdentityText(e){return(0,p.normalizeSnapshot)(e).replace(/\s+/g," ").trim().toLowerCase()}buildParsedPanePromptIdentity(e){let t=[e.kind,this.normalizePromptIdentityText(e.promptText),...e.options.map(i=>`${i.number}:${this.normalizePromptIdentityText(i.text)}`)].join("::");return{kind:"parsed",key:(0,Z.createHash)("sha256").update(t).digest("hex")}}buildAskUserQuestionPaneIdentity(e){let t=this.normalizePromptIdentityText(e.question??""),i=(e.options??[]).map(s=>this.normalizePromptIdentityText(typeof s=="string"?s:[s.label,s.description,s.preview].filter(Boolean).join(" "))),n=["ask_user_question",t,...i].join("::");return{kind:"ask_user_question",key:(0,Z.createHash)("sha256").update(n).digest("hex"),questionText:t,optionLabels:i}}async captureLivePromptPane(){let e=process.env.CODEVIBE_TMUX_SESSION;if(!e)throw new Error("CODEVIBE_TMUX_SESSION is unset");let t=e.replace(/'/g,"'\\''"),{stdout:i}=await le(`tmux capture-pane -p -e -S -40 -t '${t}'`,{timeout:3e3});return i||""}liveAskUserQuestionMatches(e,t){let i=(0,p.normalizeSnapshot)(e).split(`
6
+ `).map(h=>h.trim()).filter(Boolean);if(i.length===0||!t.questionText||!t.optionLabels)return!1;let n=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|Chat about this|Type something|Notes: press|\(esc\)|\(shift\+tab\))/i,s=i.length-1,o=-1;for(let h=s;h>=Math.max(0,s-3);h-=1)if(n.test(i[h])){o=h;break}if(o<0)return!1;let c=/^(?:[›❯▸▶➜➤]\s*)?(\d+)\.\s+(.+)$/,a=[];for(let h=Math.max(0,o-30);h<o;h+=1){let I=i[h].match(c);I&&a.push({index:h,number:Number(I[1]),firstLine:I[2]})}let m=[...a].reverse().find(h=>h.number===1);if(!m)return!1;let d=a.filter(h=>h.index>=m.index);if(d.length!==t.optionLabels.length||d.some((h,I)=>h.number!==I+1)||d.map((h,I)=>{let f=d[I+1]?.index??o,u=[h.firstLine,...i.slice(h.index+1,f).filter(y=>!n.test(y)&&!/^[─━-]+$/.test(y))];return this.normalizePromptIdentityText(u.join(" "))}).some((h,I)=>h!==t.optionLabels[I]))return!1;let l=d[0].index,v=Math.max(0,l-14);for(let h=l-1;h>=v;h-=1)if(n.test(i[h])){v=h+1;break}let S=this.normalizePromptIdentityText(i.slice(v,l).join(" "));return S===t.questionText||S.endsWith(` ${t.questionText}`)}livePromptHasNegativeAffordance(e){return(0,p.normalizeSnapshot)(e).split(`
7
+ `).map(i=>i.trim()).filter(Boolean).slice(-3).some(i=>/(?:Esc to cancel|\(esc\))/i.test(i))}liveParsedPromptIsActive(e,t){let i=(0,p.normalizeSnapshot)(e).split(`
8
+ `).map(a=>a.trim()).filter(Boolean);if(i.length===0)return!1;let n=i.length-1;if(t.kind==="yes_no"){let a=-1;for(let m=n;m>=Math.max(0,n-2);m-=1)if(/\[(?:y\/n|Y\/n|y\/N)\]/.test(i[m])){a=m;break}return a>=0}let s=/^(?:[›❯▸▶➜➤]\s*)?\d+\.\s+.+$/,o=-1;for(let a=n;a>=Math.max(0,n-6);a-=1)if(s.test(i[a])){o=a;break}if(o<0)return!1;let c=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|\(esc\)|\(shift\+tab\))/i;for(let a=n;a>=Math.max(o,n-1);a-=1)if(c.test(i[a]))return!0;return!1}async verifyLivePromptIdentity(e,t,i={}){if(!t||e.pendingPanePromptIdentity?.key!==t.key)return!1;try{let n=await this.captureLivePromptPane();return e.pendingPanePromptIdentity?.key!==t.key?!1:this.livePromptMatchesIdentity(n,t,i)}catch(n){return r.warn("Unable to verify live Claude prompt before terminal input",{sessionId:e.sessionId,promptId:e.pendingPromptId,error:n instanceof Error?n.message:String(n)}),!1}}livePromptMatchesIdentity(e,t,i={}){if(i.requireNegativeAffordance&&!this.livePromptHasNegativeAffordance(e))return!1;if(t.kind==="parsed"){let n=(0,p.parseInteractivePrompt)(e);return!!n&&this.liveParsedPromptIsActive(e,n)&&this.buildParsedPanePromptIdentity(n).key===t.key}return this.liveAskUserQuestionMatches(e,t)}async answerVerifiedInteractivePrompt(e,t,i,n){if(!await this.verifyLivePromptIdentity(e,t))return r.warn("Refusing mobile answer because the live Claude prompt changed or is uninspectable",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1;let s=n?.pressEnter??!0,o=await this.promptResponder.answerInteractivePrompt(e.sessionId,i,{pressEnter:!1});if(!o||!s)return o;if(await new Promise(c=>setTimeout(c,500)),!t||e.pendingPanePromptIdentity?.key!==t.key)return r.warn("Refusing Enter because the pending Claude prompt changed after text input",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1;try{let c=await this.captureLivePromptPane();return e.pendingPanePromptIdentity?.key!==t.key?!1:this.livePromptMatchesIdentity(c,t)?this.promptResponder.sendKey(e.sessionId,"Enter"):this.liveAnyPromptIsActive(c)?(r.warn("Refusing Enter because a different Claude prompt appeared after text input",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1):(r.info("Claude committed the mobile option without a separate Enter key",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!0)}catch(c){return r.warn("Unable to inspect the live Claude prompt after text input",{sessionId:e.sessionId,promptId:e.pendingPromptId,error:c instanceof Error?c.message:String(c)}),!1}}async sendVerifiedPromptKey(e,t,i){return await this.verifyLivePromptIdentity(e,t,{requireNegativeAffordance:i==="Escape"})?this.promptResponder.sendKey(e.sessionId,i):(r.warn("Refusing mobile key because the live Claude prompt changed or is uninspectable",{sessionId:e.sessionId,promptId:e.pendingPromptId}),!1)}liveAnyPromptIsActive(e){let t=(0,p.parseInteractivePrompt)(e);if(t&&this.liveParsedPromptIsActive(e,t))return!0;let i=(0,p.normalizeSnapshot)(e).split(`
9
+ `).map(s=>s.trim()).filter(Boolean),n=-1;for(let s=i.length-1;s>=0;s-=1)if(/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|\(esc\)|\(shift\+tab\))/i.test(i[s])){n=s;break}return n<Math.max(0,i.length-3)?!1:i.slice(Math.max(0,n-24),n).some(s=>/^(?:[›❯▸▶➜➤]\s*)?\d+\.\s+.+$/.test(s))}async sendFreeFormWhenNoChooser(e,t,i){let n=()=>this.activeSessions.get(e)===t,s=async()=>{try{return!this.liveAnyPromptIsActive(await this.captureLivePromptPane())}catch(c){return r.warn("Unable to prove the Claude chooser is absent before free-form input",{sessionId:e,error:c instanceof Error?c.message:String(c)}),!1}};return!n()||!await s()||!n()||!await this.promptResponder.answerInteractivePrompt(e,i,{pressEnter:!1})||(await new Promise(c=>setTimeout(c,500)),!n()||!await s()||!n())?!1:this.promptResponder.sendKey(e,"Enter")}clearPromptState(e){e.waitingForPromptResponse=!1,e.pendingPromptId=void 0,e.pendingSubmitMap=void 0,e.pendingPanePromptIdentity=void 0,e.pendingQuestionsQueue=void 0,e.pendingCurrentQuestion=void 0,e.pendingSynthTail=void 0,e.hasReviewScreen=void 0,e.activeAskUserQuestionFingerprint=void 0,e.promptGenerationToken=void 0}async detectWalkerActive(e){let t=process.env.CODEVIBE_TMUX_SESSION;if(!t)return r.debug("detectWalkerActive: no CODEVIBE_TMUX_SESSION env \u2014 conservatively assuming walker active",{sessionId:e}),!0;try{let{exec:i}=await import("child_process"),n=u=>new Promise((y,k)=>{i(u,{timeout:3e3},(C,_)=>{C?k(C):y({stdout:_||""})})}),{stdout:s}=await n(`tmux capture-pane -p -e -S -30 -t '${t}'`),o=/\x1B(?:\[[?!0-9;]*[A-Za-z]|\][^\x07\x1B]*(?:\x07|\x1B\\)|[()][A-B012])/g,c=s.split(`
10
+ `).map(u=>u.replace(o,"").trimEnd()),a=c.length-1;for(;a>=0&&c[a].trim()==="";)a-=1;if(a<0)return r.warn("detectWalkerActive: pane empty after ANSI strip \u2014 conservatively assuming walker active",{sessionId:e}),!0;let m=/(?:Esc to cancel|Enter to select|↑\/↓ to navigate|Tab to switch|shift\+tab|Chat about this|Type something|Notes: press|\(esc\)|\(shift\+tab\))/i,d=/^\s*(?:[›❯▸▶➜➤]\s*)?\d+\.\s+/,w=-1;for(let u=a;u>=0;u-=1)if(d.test(c[u])){w=u;break}let l=w>=0,v=-1;for(let u=a;u>=0;u-=1)if(m.test(c[u])){v=u;break}let S=v<0?1/0:a-v,h=v>=0&&S<=3,I=v>=0&&w>=0&&v>=w&&v-w<=5,f=l&&h&&I;return r.info("detectWalkerActive",{sessionId:e,walkerActive:f,hasParserBlock:l,chromeNearBottom:h,chromeFollowsBlock:I,chromeDistanceFromBottom:Number.isFinite(S)?S:-1,parserBlockEndsAt:w,lastNonBlank:a,lastChromeLineAt:v,lastLineSample:c[a]?.slice(0,80)??""}),f}catch(i){return r.warn("detectWalkerActive: tmux capture/parse failed \u2014 conservatively assuming walker active",{sessionId:e,error:i instanceof Error?i.message:String(i)}),!0}}getWalkerPosition(e){return e.pendingCurrentQuestion?._isSubmit?"on_synth":e.hasReviewScreen?(e.pendingQuestionsQueue?.length??0)>=2?"nq_mid":"nq_last":"q1"}async emitDegradedAUQNotification(e,t){try{let i=this.encryptForEmit(t,{source:"codevibe_auq_degraded"},p.EventType.ASSISTANT_RESPONSE,e);if(!i)return;let n=i.content,s=i.metadata,o=i.isEncrypted;await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.ASSISTANT_RESPONSE,source:p.EventSource.DESKTOP,content:n,metadata:s,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e}),isEncrypted:o?!0:void 0}),r.info("Emitted degraded-AUQ notification",{sessionId:e,textPreview:t.slice(0,60)})}catch(i){r.warn("emitDegradedAUQNotification failed",{sessionId:e,error:i instanceof Error?i.message:String(i)})}}async handleMobileReplyAsDismissAndPrompt(e,t,i,n,s){let o=await this.sendVerifiedPromptKey(t,i,"Escape");if(s())return!1;if(!o){try{await this.emitUserChoice(e,"AskUserQuestion dismissal failed (tmux unavailable)")}catch(a){r.warn("emitUserChoice on dismiss-ok1-failed failed",{sessionId:e,error:a instanceof Error?a.message:String(a)})}return!1}if(await new Promise(a=>setTimeout(a,200)),s())return!1;let c=await this.sendFreeFormWhenNoChooser(e,t,n);if(s())return!1;if(!c){try{await this.emitUserChoice(e,"AskUserQuestion dismissed but text-send failed; please retry")}catch(a){r.warn("emitUserChoice on dismiss-ok2-failed failed",{sessionId:e,error:a instanceof Error?a.message:String(a)})}return s()||this.promoteFingerprintAndClearPromptState(t),!1}try{await this.emitUserChoice(e,"\u2192 AskUserQuestion dismissed, sending your reply as a new prompt")}catch(a){r.warn("emitUserChoice on dismiss-success failed \u2014 continuing to promote+clear",{sessionId:e,error:a instanceof Error?a.message:String(a)})}return s()?(r.warn("handleMobileReplyAsDismissAndPrompt: aborted post-emitUserChoice (token rotated); leaving session state for new prompt",{sessionId:e}),!1):(this.promoteFingerprintAndClearPromptState(t),!0)}async handleOptionWithFollowupOnLastQ(e,t,i,n,s,o,c){let a=await this.answerVerifiedInteractivePrompt(t,i,n,{pressEnter:!1});if(c())return!1;if(!a){try{await this.emitUserChoice(e,"Failed to commit Q[N] (tmux unavailable); your reply was not sent")}catch(l){r.warn("emitUserChoice on lastQ-ok1-failed failed",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return!1}if(await new Promise(l=>setTimeout(l,200)),c())return!1;let m=t.pendingQuestionsQueue?.[0];if(!m?._isSubmit)return r.warn("Last-Q flow reached review screen without a bound SYNTH identity",{sessionId:e}),!1;t.pendingCurrentQuestion=m,t.pendingPanePromptIdentity=this.buildAskUserQuestionPaneIdentity(m);let d=await this.answerVerifiedInteractivePrompt(t,t.pendingPanePromptIdentity,"1",{pressEnter:!1});if(c())return!1;if(!d){try{await this.emitUserChoice(e,"Q[N] committed but Submit failed (tmux unavailable); please retry on desktop")}catch(l){r.warn("emitUserChoice on lastQ-ok2-failed failed",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return c()||this.promoteFingerprintAndClearPromptState(t),!1}if(await new Promise(l=>setTimeout(l,1500)),c())return!1;let w=await this.sendFreeFormWhenNoChooser(e,t,o);if(c())return!1;w||r.warn("handleOptionWithFollowupOnLastQ: followup text send failed",{sessionId:e});try{await this.emitUserChoice(e,`\u2192 ${s} + sending your reply as a new prompt`)}catch(l){r.warn("emitUserChoice on lastQ-success failed \u2014 continuing to promote+clear",{sessionId:e,error:l instanceof Error?l.message:String(l)})}return c()?(r.warn("handleOptionWithFollowupOnLastQ: aborted post-emitUserChoice (token rotated); leaving session state for new prompt",{sessionId:e}),!1):(this.promoteFingerprintAndClearPromptState(t),!0)}computeAskUserQuestionFingerprint(e){if(!(!e||typeof e!="object"))try{let t=this.stringifyCanonical(e);return(0,Z.createHash)("sha256").update(t).digest("hex")}catch(t){r.warn("Failed to fingerprint AskUserQuestion questions",{error:t instanceof Error?t.message:String(t)});return}}stringifyCanonical(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(i=>this.stringifyCanonical(i)).join(",")+"]":"{"+Object.keys(e).sort().map(i=>JSON.stringify(i)+":"+this.stringifyCanonical(e[i])).join(",")+"}"}async markEventExecutedIdempotent(e,t){e.processedEventIds||(e.processedEventIds=new Set),e.processedEventIds.add(t.eventId);try{await this.markEventExecuted(t)}catch(i){r.warn("[walker] markEventExecuted threw \u2014 relying on processedEventIds set for dedup",{sessionId:t.sessionId,eventId:t.eventId,error:String(i)})}}subscribeToOrchestrationPrompts(e){return this.openInlineSubscription({kind:"orchestration-prompt",sessionId:e,query:`subscription OnEventCreated($sessionId: ID!) {
11
11
  onEventCreated(sessionId: $sessionId) {
12
12
  eventId
13
13
  sessionId
@@ -19,15 +19,15 @@
19
19
  promptId
20
20
  isEncrypted
21
21
  }
22
- }`,onPayload:async t=>{let i=t?.onEventCreated;if(!i||i.type!==d.EventType.INTERACTIVE_PROMPT)return;let n=this.activeSessions.get(e);if(!n)return;let s=i.metadata??void 0;if(i.isEncrypted&&s&&this.sessionKey)try{let h=JSON.parse(s);if(h&&typeof h=="object"&&typeof h.encrypted=="string"){let l=d.cryptoService.decryptMetadata(h.encrypted,this.sessionKey);s=JSON.stringify(l)}}catch(h){r.warn("[v1-bridge] Failed to decrypt orchestration prompt metadata; treating as malformed",{sessionId:e,error:String(h)});return}if(!s)return;let a;try{a=JSON.parse(s)}catch{return}if(a?.prompt_kind!==d.V1_ORCHESTRATION_PROMPT_KIND)return;let o=a.payload,c=o?.gateId,u=o?.taskId,m=o?.outcome?.round;if(typeof c!="string"||typeof u!="string"||typeof m!="number"){r.error("[v1-bridge] Orchestration prompt missing required payload fields",{sessionId:e,gateId:c,taskId:u,currentRound:m});return}if(n.externallyResolvedGateIds.has(c)){n.externallyResolvedGateIds.delete(c),r.info("[v1-bridge] Skipping orchestration prompt \u2014 gate externally resolved",{sessionId:e,gateId:c});return}n.pendingOrchestrationGate={gateId:c,taskId:u,currentRound:m,promptId:i.promptId??void 0},r.info("[v1-bridge] Pending orchestration gate captured",{sessionId:e,gateId:c,taskId:u,currentRound:m})}})}async subscribeToApplyUserDecision(e){let t=e.sessionId,i=await this.getOrCreateOrchestrationClient(e),n=a=>{let o=a.decision,c=this.activeSessions.get(t);if(!c)return;let u=Fe(a.gateId,{originatedDecisions:c.originatedDecisions,externallyResolvedGateIds:c.externallyResolvedGateIds});if(u.kind==="unknown_gate"){r.warn("[v1-bridge] onApplyUserDecision event missing gateId",{sessionId:t,decision:o});return}c.originatedDecisions=u.originated,c.externallyResolvedGateIds=u.external;let m=u.gateId;if(u.kind==="own_echo"){r.info("[v1-bridge] applyUserDecision own-echo received",{sessionId:t,gateId:m,decision:o}),c.pendingOrchestrationGate?.gateId===m&&(c.pendingOrchestrationGate=void 0);return}c.pendingOrchestrationGate?.gateId===m&&(c.pendingOrchestrationGate=void 0),r.info("[v1-bridge] applyUserDecision foreign event \u2014 emitting banner",{sessionId:t,gateId:m,decision:o}),this.emitOrchestrationBanner(o)},s=null;try{s=await i.onApplyUserDecision(n),r.info("[v1-bridge] SDK onApplyUserDecision subscription started",{sessionId:t})}catch(a){throw r.warn("[v1-bridge] SDK onApplyUserDecision subscription failed",{sessionId:t,error:String(a)}),a}return{stop:()=>{if(s){try{s()}catch(a){r.warn("[v1-bridge] SDK onApplyUserDecision unsubscribe threw",{sessionId:t,error:String(a)})}s=null}}}}async getOrCreateOrchestrationClient(e){if(e.orchestrationClient)return e.orchestrationClient;let t=await d.keychainManager.getTokens((0,d.getEnvironment)());if(!t?.idToken)throw new Error("No Cognito ID token available \u2014 plugin must be logged in");if(!this.sessionKey)throw new Error("No session encryption key available \u2014 V1 bridge cannot start without sessionKey");let i=(0,d.getConfig)(),n=this.sessionKey,s=await Oe.OrchestrationClient.connect({appsyncUrl:i.aws.appsyncUrl,cognitoIdToken:t.idToken,tokenProvider:async()=>(await d.keychainManager.getTokens((0,d.getEnvironment)()))?.idToken??t.idToken,sessionId:e.sessionId,sessionKeyB64:n,webSocketFactory:((a,o)=>new ae.default(a,o))});return e.orchestrationClient=s,r.info("[v1-bridge] OrchestrationClient connected",{sessionId:e.sessionId}),s}async emitOrchestrationBanner(e){let t=process.env[J],i=Ne(e);if(r.info(i),!t){r.warn("[v1-bridge] No tmux session \u2014 banner only logged, not displayed",{expectedEnv:J});return}try{await oe(`tmux display-message -t "${t}" -- ${JSON.stringify(i)}`)}catch(n){r.warn("[v1-bridge] tmux display-message failed",{tmuxSession:t,error:String(n)})}}async handleOrchestrationPromptResponse(e,t,i){let n=e.pendingOrchestrationGate;if(!n)return!1;if(Ue(e))return r.warn("[v1-bridge] Decision watcher absent \u2014 falling through to free-text routing",{sessionId:e.sessionId,gateId:n.gateId}),e.pendingOrchestrationGate=void 0,!1;if(e.externallyResolvedGateIds.has(n.gateId))return e.externallyResolvedGateIds.delete(n.gateId),e.pendingOrchestrationGate=void 0,r.info("[v1-bridge] User typed AFTER foreign decision \u2014 discarding without submit",{sessionId:e.sessionId,gateId:n.gateId}),await this.markEventExecuted(i),!0;let a=t.trim().match(/^(\d+)$/);if(!a)return await this.sendPromptError(e.sessionId,"Orchestration prompt requires a numeric option (1, 2, or 3)."),!0;let o=parseInt(a[1],10),c=(0,d.mapOptionToUserDecisionKind)(o);if(c===null)return await this.sendPromptError(e.sessionId,`Option ${o} is out of range for this orchestration prompt (1, 2, or 3).`),!0;e.originatedDecisions.add(n.gateId);try{await this.originateOrchestrationDecision(e,n,c),e.pendingOrchestrationGate=void 0;try{let u=this.encryptForEmit(`Orchestration decision submitted: ${c}`,{orchestrationDecision:!0,gateId:n.gateId,decision:c},d.EventType.NOTIFICATION,e.sessionId);u&&await this.appSyncClient.createEvent({sessionId:e.sessionId,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:u.content,metadata:u.metadata,isEncrypted:u.isEncrypted,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e.sessionId})})}catch(u){r.warn("[v1-bridge] Failed to emit decision-submitted notification",{sessionId:e.sessionId,error:String(u)})}await this.markEventExecuted(i)}catch(u){e.originatedDecisions.delete(n.gateId),r.error("[v1-bridge] applyUserDecision mutation failed; reverting state",{sessionId:e.sessionId,gateId:n.gateId,error:String(u)}),await this.sendPromptError(e.sessionId,`Failed to submit orchestration decision: ${u?.message??String(u)}`)}return!0}async originateOrchestrationDecision(e,t,i){let n=await this.getOrCreateOrchestrationClient(e),s=i;await n.applyUserDecision({taskId:t.taskId,gateId:t.gateId,currentRound:t.currentRound,decision:s}),r.info("[v1-bridge] applyUserDecision submitted",{sessionId:e.sessionId,gateId:t.gateId,decision:s})}openInlineSubscription(e){let{kind:t,sessionId:i,query:n,onPayload:s}=e,a=(0,d.getConfig)(),o=require("uuid").v4(),c=new URL(a.aws.appsyncUrl),m=/\.appsync-api\.[^.]+\.amazonaws\.com$/.test(c.host)?a.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"):`wss://${c.host}/graphql/realtime`,h=!1,l=null;return(async()=>{let g=await d.keychainManager.getTokens((0,d.getEnvironment)());if(!g?.idToken){r.warn(`[v1-bridge:${t}] No ID token \u2014 skipping subscription`,{sessionId:i});return}let p={host:c.host,Authorization:g.idToken},S=Buffer.from(JSON.stringify(p)).toString("base64"),N=Buffer.from(JSON.stringify({})).toString("base64"),w=`${m}?header=${S}&payload=${N}`;l=new ae.default(w,["graphql-ws"]),l.on("open",()=>{h||(r.info(`[v1-bridge:${t}] WebSocket connected`,{sessionId:i}),l.send(JSON.stringify({type:"connection_init"})))}),l.on("message",async v=>{if(!h)try{let C=JSON.parse(v.toString());switch(C.type){case"connection_ack":l.send(JSON.stringify({id:o,type:"start",payload:{data:JSON.stringify({query:n,variables:{sessionId:i}}),extensions:{authorization:p}}}));break;case"start_ack":r.info(`[v1-bridge:${t}] Subscription started`,{sessionId:i});break;case"data":try{await s(C.payload?.data)}catch(b){r.warn(`[v1-bridge:${t}] onPayload handler threw`,{sessionId:i,error:String(b)})}break;case"ka":break;case"error":r.warn(`[v1-bridge:${t}] Subscription error`,{sessionId:i,error:C.payload?.errors?.[0]?.message??"unknown"});break}}catch(C){r.warn(`[v1-bridge:${t}] Failed to parse message`,{sessionId:i,error:String(C)})}}),l.on("error",v=>{r.warn(`[v1-bridge:${t}] WebSocket error`,{sessionId:i,error:v.message})}),l.on("close",v=>{r.info(`[v1-bridge:${t}] WebSocket closed`,{sessionId:i,code:v})})})().catch(g=>{r.warn(`[v1-bridge:${t}] Initial open failed`,{sessionId:i,error:String(g)})}),{stop:()=>{if(h=!0,l){try{l.readyState===ae.default.OPEN&&l.send(JSON.stringify({id:o,type:"stop"})),l.close(1e3)}catch{}l=null}}}}isSessionLimitExceeded(e){return this.getErrorMessage(e).includes("SESSION_LIMIT_EXCEEDED")}isUsageLimitExceeded(e){let t=this.getErrorMessage(e);return t.includes("MESSAGE_LIMIT_EXCEEDED")||t.includes("IMAGE_LIMIT_EXCEEDED")}getErrorMessage(e){if(e instanceof Error)return e.message;if(typeof e=="object"&&e!==null){let t=e;if(t.errors&&Array.isArray(t.errors))return t.errors.map(i=>i.message||"").join(" ");if(typeof t.message=="string")return t.message}return String(e)}displaySubscriptionLimitError(e,t){let i=this.getErrorMessage(e),n="",s=i.match(/for your (\w+) plan/i);s&&(n=` (${s[1]} tier)`);let a="",o=i.match(/of (\d+)/);switch(o&&(a=` [Limit: ${o[1]}]`),console.log(`
23
- `+"=".repeat(60)),console.log("\u26A0\uFE0F SUBSCRIPTION LIMIT REACHED"),console.log("=".repeat(60)),t){case"session":console.log(`You have reached the maximum number of active sessions${n}.`),console.log(`${a}`),console.log(`
24
- To continue, please:`),console.log(" \u2022 Close an existing Claude Code session, or"),console.log(" \u2022 Upgrade your subscription in the CodeVibe iOS app");break;case"message":console.log(`You have reached your monthly message limit${n}.`),console.log(`${a}`),console.log(`
25
- To continue, please:`),console.log(" \u2022 Wait until your usage resets next month, or"),console.log(" \u2022 Upgrade your subscription in the CodeVibe iOS app");break;case"image":console.log(`You have reached your monthly image attachment limit${n}.`),console.log(`${a}`),console.log(`
22
+ }`,onPayload:async t=>{let i=t?.onEventCreated;if(!i||i.type!==p.EventType.INTERACTIVE_PROMPT)return;let n=this.activeSessions.get(e);if(!n)return;let s=i.metadata??void 0;if(i.isEncrypted&&s&&this.sessionKey)try{let w=JSON.parse(s);if(w&&typeof w=="object"&&typeof w.encrypted=="string"){let l=p.cryptoService.decryptMetadata(w.encrypted,this.sessionKey);s=JSON.stringify(l)}}catch(w){r.warn("[v1-bridge] Failed to decrypt orchestration prompt metadata; treating as malformed",{sessionId:e,error:String(w)});return}if(!s)return;let o;try{o=JSON.parse(s)}catch{return}if(o?.prompt_kind!==p.V1_ORCHESTRATION_PROMPT_KIND)return;let c=o.payload,a=c?.gateId,m=c?.taskId,d=c?.outcome?.round;if(typeof a!="string"||typeof m!="string"||typeof d!="number"){r.error("[v1-bridge] Orchestration prompt missing required payload fields",{sessionId:e,gateId:a,taskId:m,currentRound:d});return}if(n.externallyResolvedGateIds.has(a)){n.externallyResolvedGateIds.delete(a),r.info("[v1-bridge] Skipping orchestration prompt \u2014 gate externally resolved",{sessionId:e,gateId:a});return}n.pendingOrchestrationGate={gateId:a,taskId:m,currentRound:d,promptId:i.promptId??void 0},r.info("[v1-bridge] Pending orchestration gate captured",{sessionId:e,gateId:a,taskId:m,currentRound:d})}})}async subscribeToApplyUserDecision(e){let t=e.sessionId,i=await this.getOrCreateOrchestrationClient(e),n=o=>{let c=o.decision,a=this.activeSessions.get(t);if(!a)return;let m=qe(o.gateId,{originatedDecisions:a.originatedDecisions,externallyResolvedGateIds:a.externallyResolvedGateIds});if(m.kind==="unknown_gate"){r.warn("[v1-bridge] onApplyUserDecision event missing gateId",{sessionId:t,decision:c});return}a.originatedDecisions=m.originated,a.externallyResolvedGateIds=m.external;let d=m.gateId;if(m.kind==="own_echo"){r.info("[v1-bridge] applyUserDecision own-echo received",{sessionId:t,gateId:d,decision:c}),a.pendingOrchestrationGate?.gateId===d&&(a.pendingOrchestrationGate=void 0);return}a.pendingOrchestrationGate?.gateId===d&&(a.pendingOrchestrationGate=void 0),r.info("[v1-bridge] applyUserDecision foreign event \u2014 emitting banner",{sessionId:t,gateId:d,decision:c}),this.emitOrchestrationBanner(c)},s=null;try{s=await i.onApplyUserDecision(n),r.info("[v1-bridge] SDK onApplyUserDecision subscription started",{sessionId:t})}catch(o){throw r.warn("[v1-bridge] SDK onApplyUserDecision subscription failed",{sessionId:t,error:String(o)}),o}return{stop:()=>{if(s){try{s()}catch(o){r.warn("[v1-bridge] SDK onApplyUserDecision unsubscribe threw",{sessionId:t,error:String(o)})}s=null}}}}async getOrCreateOrchestrationClient(e){if(e.orchestrationClient)return e.orchestrationClient;let t=await p.keychainManager.getTokens((0,p.getEnvironment)());if(!t?.idToken)throw new Error("No Cognito ID token available \u2014 plugin must be logged in");if(!this.sessionKey)throw new Error("No session encryption key available \u2014 V1 bridge cannot start without sessionKey");let i=(0,p.getConfig)(),n=this.sessionKey,s=await Ve.OrchestrationClient.connect({appsyncUrl:i.aws.appsyncUrl,cognitoIdToken:t.idToken,tokenProvider:async()=>(await p.keychainManager.getTokens((0,p.getEnvironment)()))?.idToken??t.idToken,sessionId:e.sessionId,sessionKeyB64:n,webSocketFactory:((o,c)=>new ue.default(o,c))});return e.orchestrationClient=s,r.info("[v1-bridge] OrchestrationClient connected",{sessionId:e.sessionId}),s}async emitOrchestrationBanner(e){let t=process.env[X],i=He(e);if(r.info(i),!t){r.warn("[v1-bridge] No tmux session \u2014 banner only logged, not displayed",{expectedEnv:X});return}try{await le(`tmux display-message -t "${t}" -- ${JSON.stringify(i)}`)}catch(n){r.warn("[v1-bridge] tmux display-message failed",{tmuxSession:t,error:String(n)})}}async handleOrchestrationPromptResponse(e,t,i){let n=e.pendingOrchestrationGate;if(!n)return!1;if(We(e))return r.warn("[v1-bridge] Decision watcher absent \u2014 falling through to free-text routing",{sessionId:e.sessionId,gateId:n.gateId}),e.pendingOrchestrationGate=void 0,!1;if(e.externallyResolvedGateIds.has(n.gateId))return e.externallyResolvedGateIds.delete(n.gateId),e.pendingOrchestrationGate=void 0,r.info("[v1-bridge] User typed AFTER foreign decision \u2014 discarding without submit",{sessionId:e.sessionId,gateId:n.gateId}),await this.markEventExecuted(i),!0;let o=t.trim().match(/^(\d+)$/);if(!o)return await this.sendPromptError(e.sessionId,"Orchestration prompt requires a numeric option (1, 2, or 3)."),!0;let c=parseInt(o[1],10),a=(0,p.mapOptionToUserDecisionKind)(c);if(a===null)return await this.sendPromptError(e.sessionId,`Option ${c} is out of range for this orchestration prompt (1, 2, or 3).`),!0;e.originatedDecisions.add(n.gateId);try{await this.originateOrchestrationDecision(e,n,a),e.pendingOrchestrationGate=void 0;try{let m=this.encryptForEmit(`Orchestration decision submitted: ${a}`,{orchestrationDecision:!0,gateId:n.gateId,decision:a},p.EventType.NOTIFICATION,e.sessionId);m&&await this.appSyncClient.createEvent({sessionId:e.sessionId,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:m.content,metadata:m.metadata,isEncrypted:m.isEncrypted,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e.sessionId})})}catch(m){r.warn("[v1-bridge] Failed to emit decision-submitted notification",{sessionId:e.sessionId,error:String(m)})}await this.markEventExecuted(i)}catch(m){e.originatedDecisions.delete(n.gateId),r.error("[v1-bridge] applyUserDecision mutation failed; reverting state",{sessionId:e.sessionId,gateId:n.gateId,error:String(m)}),await this.sendPromptError(e.sessionId,`Failed to submit orchestration decision: ${m?.message??String(m)}`)}return!0}async originateOrchestrationDecision(e,t,i){let n=await this.getOrCreateOrchestrationClient(e),s=i;await n.applyUserDecision({taskId:t.taskId,gateId:t.gateId,currentRound:t.currentRound,decision:s}),r.info("[v1-bridge] applyUserDecision submitted",{sessionId:e.sessionId,gateId:t.gateId,decision:s})}openInlineSubscription(e){let{kind:t,sessionId:i,query:n,onPayload:s}=e,o=(0,p.getConfig)(),c=require("uuid").v4(),a=new URL(o.aws.appsyncUrl),d=/\.appsync-api\.[^.]+\.amazonaws\.com$/.test(a.host)?o.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"):`wss://${a.host}/graphql/realtime`,w=!1,l=null;return(async()=>{let S=await p.keychainManager.getTokens((0,p.getEnvironment)());if(!S?.idToken){r.warn(`[v1-bridge:${t}] No ID token \u2014 skipping subscription`,{sessionId:i});return}let h={host:a.host,Authorization:S.idToken},I=Buffer.from(JSON.stringify(h)).toString("base64"),f=Buffer.from(JSON.stringify({})).toString("base64"),u=`${d}?header=${I}&payload=${f}`;l=new ue.default(u,["graphql-ws"]),l.on("open",()=>{w||(r.info(`[v1-bridge:${t}] WebSocket connected`,{sessionId:i}),l.send(JSON.stringify({type:"connection_init"})))}),l.on("message",async y=>{if(!w)try{let k=JSON.parse(y.toString());switch(k.type){case"connection_ack":l.send(JSON.stringify({id:c,type:"start",payload:{data:JSON.stringify({query:n,variables:{sessionId:i}}),extensions:{authorization:h}}}));break;case"start_ack":r.info(`[v1-bridge:${t}] Subscription started`,{sessionId:i});break;case"data":try{await s(k.payload?.data)}catch(C){r.warn(`[v1-bridge:${t}] onPayload handler threw`,{sessionId:i,error:String(C)})}break;case"ka":break;case"error":r.warn(`[v1-bridge:${t}] Subscription error`,{sessionId:i,error:k.payload?.errors?.[0]?.message??"unknown"});break}}catch(k){r.warn(`[v1-bridge:${t}] Failed to parse message`,{sessionId:i,error:String(k)})}}),l.on("error",y=>{r.warn(`[v1-bridge:${t}] WebSocket error`,{sessionId:i,error:y.message})}),l.on("close",y=>{r.info(`[v1-bridge:${t}] WebSocket closed`,{sessionId:i,code:y})})})().catch(S=>{r.warn(`[v1-bridge:${t}] Initial open failed`,{sessionId:i,error:String(S)})}),{stop:()=>{if(w=!0,l){try{l.readyState===ue.default.OPEN&&l.send(JSON.stringify({id:c,type:"stop"})),l.close(1e3)}catch{}l=null}}}}isSessionLimitExceeded(e){return this.getErrorMessage(e).includes("SESSION_LIMIT_EXCEEDED")}isUsageLimitExceeded(e){let t=this.getErrorMessage(e);return t.includes("MESSAGE_LIMIT_EXCEEDED")||t.includes("IMAGE_LIMIT_EXCEEDED")}getErrorMessage(e){if(e instanceof Error)return e.message;if(typeof e=="object"&&e!==null){let t=e;if(t.errors&&Array.isArray(t.errors))return t.errors.map(i=>i.message||"").join(" ");if(typeof t.message=="string")return t.message}return String(e)}displaySubscriptionLimitError(e,t){let i=this.getErrorMessage(e),n="",s=i.match(/for your (\w+) plan/i);s&&(n=` (${s[1]} tier)`);let o="",c=i.match(/of (\d+)/);switch(c&&(o=` [Limit: ${c[1]}]`),console.log(`
23
+ `+"=".repeat(60)),console.log("\u26A0\uFE0F SUBSCRIPTION LIMIT REACHED"),console.log("=".repeat(60)),t){case"session":console.log(`You have reached the maximum number of active sessions${n}.`),console.log(`${o}`),console.log(`
24
+ To continue, please:`),console.log(" \u2022 Close an existing Claude Code session, or"),console.log(" \u2022 Upgrade your subscription in the CodeVibe iOS app");break;case"message":console.log(`You have reached your monthly message limit${n}.`),console.log(`${o}`),console.log(`
25
+ To continue, please:`),console.log(" \u2022 Wait until your usage resets next month, or"),console.log(" \u2022 Upgrade your subscription in the CodeVibe iOS app");break;case"image":console.log(`You have reached your monthly image attachment limit${n}.`),console.log(`${o}`),console.log(`
26
26
  To continue, please:`),console.log(" \u2022 Wait until your usage resets next month, or"),console.log(" \u2022 Upgrade your subscription in the CodeVibe iOS app");break}console.log(`
27
27
  Note: You can still use Claude Code normally from your desktop.`),console.log("This limit only affects syncing with the mobile app."),console.log("=".repeat(60)+`
28
- `),r.error("Subscription limit exceeded",{limitType:t,errorMessage:i})}async downloadAttachment(e,t,i){try{let n=e.isEncrypted??i??!1;r.info("Downloading attachment - START",{id:e.id,type:e.type,filename:e.filename,s3Key:e.s3Key,attachmentIsEncrypted:e.isEncrypted,eventIsEncrypted:i,shouldDecrypt:n,hasSessionKey:!!this.sessionKey});let{downloadUrl:s}=await this.appSyncClient.getAttachmentDownloadUrl(e.s3Key),a=await fetch(s);if(!a.ok)throw new Error(`Failed to download attachment: ${a.status} ${a.statusText}`);let o=Buffer.from(await a.arrayBuffer());if(r.info("Attachment downloaded",{id:e.id,downloadedSize:o.length,first20Bytes:o.slice(0,20).toString("hex")}),r.info("Checking decryption conditions",{id:e.id,shouldDecrypt:n,hasSessionKey:!!this.sessionKey,willDecrypt:!!(n&&this.sessionKey)}),n&&this.sessionKey)try{r.info("Decrypting attachment",{id:e.id,encryptedSize:o.length}),o=d.cryptoService.decryptData(o,this.sessionKey),r.info("Attachment decrypted successfully",{id:e.id,decryptedSize:o.length,first20Bytes:o.slice(0,20).toString("hex")})}catch(f){throw r.error("Failed to decrypt attachment:",{id:e.id,error:f}),new Error("Failed to decrypt attachment")}else n&&!this.sessionKey?r.warn("Cannot decrypt attachment - no session key available",{id:e.id}):r.info("Skipping decryption - attachment not encrypted or no session key",{id:e.id,shouldDecrypt:n,hasSessionKey:!!this.sessionKey});let c=G.join(W.tmpdir(),"codevibe-claude",t);D.existsSync(c)||D.mkdirSync(c,{recursive:!0});let u="",m=e.filename;if(n&&e.filename&&this.sessionKey)try{m=d.cryptoService.decryptContent(e.filename,this.sessionKey)}catch{m=e.filename}if(m){let f=G.extname(m);f&&(u=f)}u||(u={"image/jpeg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp","image/heic":".heic","application/pdf":".pdf"}[e.type]||".bin");let h=`attachment-${e.id}${u}`,l=G.join(c,h);return D.writeFileSync(l,o),r.info("Attachment saved to temp file",{id:e.id,filePath:l,size:o.length,wasDecrypted:n&&!!this.sessionKey}),l}catch(n){return r.error("Failed to download attachment:",{id:e.id,error:n}),null}}async executeMobilePrompt(e,t){let i=this.activeSessions.get(e);if(!i){r.warn("Cannot execute mobile prompt without the current Claude session state",{sessionId:e});return}let n=t.content||"",s=t.attachments||[];r.info("Executing mobile prompt via tmux",{sessionId:e,promptLength:n.length,attachmentCount:s.length});let a=[];if(s.length>0){r.info("Downloading attachments for prompt",{count:s.length});for(let o of s){let c=await this.downloadAttachment(o,e,t.isEncrypted);c&&a.push(c)}if(a.length>0){let o=a.map(c=>`[Attached file: ${c}]`).join(`
29
- `);n?n=`${o}
28
+ `),r.error("Subscription limit exceeded",{limitType:t,errorMessage:i})}async downloadAttachment(e,t,i){try{let n=e.isEncrypted??i??!1;r.info("Downloading attachment - START",{id:e.id,type:e.type,filename:e.filename,s3Key:e.s3Key,attachmentIsEncrypted:e.isEncrypted,eventIsEncrypted:i,shouldDecrypt:n,hasSessionKey:!!this.sessionKey});let{downloadUrl:s}=await this.appSyncClient.getAttachmentDownloadUrl(e.s3Key),o=await fetch(s);if(!o.ok)throw new Error(`Failed to download attachment: ${o.status} ${o.statusText}`);let c=Buffer.from(await o.arrayBuffer());if(r.info("Attachment downloaded",{id:e.id,downloadedSize:c.length,first20Bytes:c.slice(0,20).toString("hex")}),r.info("Checking decryption conditions",{id:e.id,shouldDecrypt:n,hasSessionKey:!!this.sessionKey,willDecrypt:!!(n&&this.sessionKey)}),n&&this.sessionKey)try{r.info("Decrypting attachment",{id:e.id,encryptedSize:c.length}),c=p.cryptoService.decryptData(c,this.sessionKey),r.info("Attachment decrypted successfully",{id:e.id,decryptedSize:c.length,first20Bytes:c.slice(0,20).toString("hex")})}catch(v){throw r.error("Failed to decrypt attachment:",{id:e.id,error:v}),new Error("Failed to decrypt attachment")}else n&&!this.sessionKey?r.warn("Cannot decrypt attachment - no session key available",{id:e.id}):r.info("Skipping decryption - attachment not encrypted or no session key",{id:e.id,shouldDecrypt:n,hasSessionKey:!!this.sessionKey});let a=G.join(j.tmpdir(),"codevibe-claude",t);L.existsSync(a)||L.mkdirSync(a,{recursive:!0});let m="",d=e.filename;if(n&&e.filename&&this.sessionKey)try{d=p.cryptoService.decryptContent(e.filename,this.sessionKey)}catch{d=e.filename}if(d){let v=G.extname(d);v&&(m=v)}m||(m={"image/jpeg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp","image/heic":".heic","application/pdf":".pdf"}[e.type]||".bin");let w=`attachment-${e.id}${m}`,l=G.join(a,w);return L.writeFileSync(l,c),r.info("Attachment saved to temp file",{id:e.id,filePath:l,size:c.length,wasDecrypted:n&&!!this.sessionKey}),l}catch(n){return r.error("Failed to download attachment:",{id:e.id,error:n}),null}}async executeMobilePrompt(e,t){let i=this.activeSessions.get(e);if(!i){r.warn("Cannot execute mobile prompt without the current Claude session state",{sessionId:e});return}let n=t.content||"",s=t.attachments||[];r.info("Executing mobile prompt via tmux",{sessionId:e,promptLength:n.length,attachmentCount:s.length});let o=[];if(s.length>0){r.info("Downloading attachments for prompt",{count:s.length});for(let c of s){let a=await this.downloadAttachment(c,e,t.isEncrypted);a&&o.push(a)}if(o.length>0){let c=o.map(a=>`[Attached file: ${a}]`).join(`
29
+ `);n?n=`${c}
30
30
 
31
- ${n}`:n=`${o}
31
+ ${n}`:n=`${c}
32
32
 
33
- Please analyze the attached file(s).`,r.info("Prompt updated with attachment paths",{attachmentCount:a.length,newPromptLength:n.length})}}this.trackMobilePrompt(e,n);try{if(await this.sendFreeFormWhenNoChooser(e,i,n)){try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:d.DeliveryStatus.EXECUTED}),r.info("Event marked as EXECUTED",{eventId:t.eventId})}catch(m){r.warn("Failed to mark event as EXECUTED",{eventId:t.eventId,error:m})}r.info("Mobile prompt sent successfully",{sessionId:e});let c=a.length>0?`Prompt with ${a.length} attachment(s) sent to Claude Code`:`Prompt "${n.substring(0,50)}${n.length>50?"...":""}" sent to Claude Code`,u=this.encryptForEmit(c,{mobilePrompt:!0,attachmentCount:a.length},d.EventType.NOTIFICATION,e);u&&await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:u.content,metadata:u.metadata,isEncrypted:u.isEncrypted,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e})})}else{r.error("Failed to send mobile prompt",{sessionId:e});let c=this.encryptForEmit("Failed to send prompt to Claude Code",{error:!0},d.EventType.NOTIFICATION,e);c&&await this.appSyncClient.createEvent({sessionId:e,type:d.EventType.NOTIFICATION,source:d.EventSource.DESKTOP,content:c.content,metadata:c.metadata,isEncrypted:c.isEncrypted,timestamp:(0,d.prepareEventTimestamp)({orderingKey:e})})}}catch(o){r.error("Failed to execute mobile prompt:",o)}}};async function nt(){let y=process.argv[2]||process.env.CLAUDE_SESSION_ID;y?r.info(`Starting MCP server for session: ${y}`):r.info("Starting MCP server without initial session ID (will be set on SessionStart)");let e=new ce(y);try{await e.start();let t=e.getPort();console.log(`PORT=${t}`);let i=!1,n=async s=>{if(i){r.info("Shutdown already in progress, ignoring additional signal");return}i=!0,r.info(`Received ${s} signal, stopping server...`);try{await e.stop(),r.info("Graceful shutdown completed"),process.exit(_e(s))}catch(a){r.error("Error during shutdown:",a),process.exit(1)}};process.on("SIGINT",()=>n("SIGINT")),process.on("SIGTERM",()=>n("SIGTERM")),process.on("SIGHUP",()=>n("SIGHUP")),(0,d.installDaemonProcessGuards)(r,{onFatal:()=>{n("uncaughtException")}})}catch(t){r.error("Failed to start MCP Server:",t),process.exit(1)}}function _e(y){return y==="uncaughtException"?1:0}function it(y){let t=y.trim().match(/^(\d+)$/);if(!t)return{kind:"error",reason:"non_numeric"};let i=parseInt(t[1],10),n=(0,d.mapOptionToUserDecisionKind)(i);return n===null?{kind:"error",reason:"out_of_range",optionNumber:i}:{kind:"submit",optionNumber:i,decision:n}}function Fe(y,e){if(typeof y!="string")return{kind:"unknown_gate"};let t=y,i=new Set(e.originatedDecisions),n=new Set(e.externallyResolvedGateIds);return i.has(t)?(i.delete(t),n.add(t),{kind:"own_echo",gateId:t,originated:i,external:n}):(n.add(t),{kind:"foreign",gateId:t,originated:i,external:n})}function Ne(y){return`[CodeVibe] Decision recorded on another device: ${y.toUpperCase()} \u2014 type any number to dismiss the open AskUserQuestion`}function Ue(y){return!!y.pendingOrchestrationGate&&!y.orchestrationDecisionWatcher}function Me(y,e=3){let t=y.trim(),i=t.match(/^(\d+)$/);if(i){let s=parseInt(i[1]);if(s>=1&&s<=e)return{action:"select_option",option:i[1]}}let n=t.match(/^(\d+)[,.:;\-\s\n]+(.+)$/s);if(n){let s=parseInt(n[1]);if(s>=1&&s<=e)return{action:"option_with_followup",option:n[1],followUpText:n[2].trim()}}return{action:"send_as_response"}}require.main===module&&nt().catch(y=>{r.error("Unhandled error in main:",y),process.exit(1)});0&&(module.exports={McpServer,classifyApplyUserDecisionEvent,classifyOrchestrationResponse,formatOrchestrationBannerText,parseInteractivePromptInput,shouldFallThroughForBridgeUnavailable,shutdownExitCode});
33
+ Please analyze the attached file(s).`,r.info("Prompt updated with attachment paths",{attachmentCount:o.length,newPromptLength:n.length})}}this.trackMobilePrompt(e,n);try{if(await this.sendFreeFormWhenNoChooser(e,i,n)){try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:p.DeliveryStatus.EXECUTED}),r.info("Event marked as EXECUTED",{eventId:t.eventId})}catch(d){r.warn("Failed to mark event as EXECUTED",{eventId:t.eventId,error:d})}r.info("Mobile prompt sent successfully",{sessionId:e});let a=o.length>0?`Prompt with ${o.length} attachment(s) sent to Claude Code`:`Prompt "${n.substring(0,50)}${n.length>50?"...":""}" sent to Claude Code`,m=this.encryptForEmit(a,{mobilePrompt:!0,attachmentCount:o.length},p.EventType.NOTIFICATION,e);m&&await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:m.content,metadata:m.metadata,isEncrypted:m.isEncrypted,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e})})}else{r.error("Failed to send mobile prompt",{sessionId:e});let a=this.encryptForEmit("Failed to send prompt to Claude Code",{error:!0},p.EventType.NOTIFICATION,e);a&&await this.appSyncClient.createEvent({sessionId:e,type:p.EventType.NOTIFICATION,source:p.EventSource.DESKTOP,content:a.content,metadata:a.metadata,isEncrypted:a.isEncrypted,timestamp:(0,p.prepareEventTimestamp)({orderingKey:e})})}}catch(c){r.error("Failed to execute mobile prompt:",c)}}};async function ht(){let g=process.argv[2]||process.env.CLAUDE_SESSION_ID;g?r.info(`Starting MCP server for session: ${g}`):r.info("Starting MCP server without initial session ID (will be set on SessionStart)");let e=new me(g);try{await e.start();let t=e.getPort();console.log(`PORT=${t}`);let i=!1,n=async s=>{if(i){r.info("Shutdown already in progress, ignoring additional signal");return}i=!0,r.info(`Received ${s} signal, stopping server...`);try{await e.stop(),r.info("Graceful shutdown completed"),process.exit(Ge(s))}catch(o){r.error("Error during shutdown:",o),process.exit(1)}};process.on("SIGINT",()=>n("SIGINT")),process.on("SIGTERM",()=>n("SIGTERM")),process.on("SIGHUP",()=>n("SIGHUP")),(0,p.installDaemonProcessGuards)(r,{onFatal:()=>{n("uncaughtException")}})}catch(t){r.error("Failed to start MCP Server:",t),process.exit(1)}}function Ge(g){return g==="uncaughtException"?1:0}function gt(g){let t=g.trim().match(/^(\d+)$/);if(!t)return{kind:"error",reason:"non_numeric"};let i=parseInt(t[1],10),n=(0,p.mapOptionToUserDecisionKind)(i);return n===null?{kind:"error",reason:"out_of_range",optionNumber:i}:{kind:"submit",optionNumber:i,decision:n}}function qe(g,e){if(typeof g!="string")return{kind:"unknown_gate"};let t=g,i=new Set(e.originatedDecisions),n=new Set(e.externallyResolvedGateIds);return i.has(t)?(i.delete(t),n.add(t),{kind:"own_echo",gateId:t,originated:i,external:n}):(n.add(t),{kind:"foreign",gateId:t,originated:i,external:n})}function He(g){return`[CodeVibe] Decision recorded on another device: ${g.toUpperCase()} \u2014 type any number to dismiss the open AskUserQuestion`}function We(g){return!!g.pendingOrchestrationGate&&!g.orchestrationDecisionWatcher}function je(g,e=3){let t=g.trim(),i=t.match(/^(\d+)$/);if(i){let s=parseInt(i[1]);if(s>=1&&s<=e)return{action:"select_option",option:i[1]}}let n=t.match(/^(\d+)[,.:;\-\s\n]+(.+)$/s);if(n){let s=parseInt(n[1]);if(s>=1&&s<=e)return{action:"option_with_followup",option:n[1],followUpText:n[2].trim()}}return{action:"send_as_response"}}require.main===module&&ht().catch(g=>{r.error("Unhandled error in main:",g),process.exit(1)});0&&(module.exports={McpServer,classifyApplyUserDecisionEvent,classifyOrchestrationResponse,formatOrchestrationBannerText,parseInteractivePromptInput,shouldFallThroughForBridgeUnavailable,shutdownExitCode});