@linghun/tui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (692) hide show
  1. package/LICENSE +190 -0
  2. package/dist/agent-completion-finalizer.d.ts +31 -0
  3. package/dist/agent-completion-finalizer.d.ts.map +1 -0
  4. package/dist/agent-completion-finalizer.js +20 -0
  5. package/dist/agent-handoff-safety.d.ts +13 -0
  6. package/dist/agent-handoff-safety.d.ts.map +1 -0
  7. package/dist/agent-handoff-safety.js +43 -0
  8. package/dist/agent-workflow-registry.d.ts +45 -0
  9. package/dist/agent-workflow-registry.d.ts.map +1 -0
  10. package/dist/agent-workflow-registry.js +12 -0
  11. package/dist/architecture-boundary.d.ts +151 -0
  12. package/dist/architecture-boundary.d.ts.map +1 -0
  13. package/dist/architecture-boundary.js +24 -0
  14. package/dist/architecture-runtime.d.ts +56 -0
  15. package/dist/architecture-runtime.d.ts.map +1 -0
  16. package/dist/architecture-runtime.js +18 -0
  17. package/dist/background-control-runtime.d.ts +29 -0
  18. package/dist/background-control-runtime.d.ts.map +1 -0
  19. package/dist/background-control-runtime.js +148 -0
  20. package/dist/bash-subcommand-parser.d.ts +34 -0
  21. package/dist/bash-subcommand-parser.d.ts.map +1 -0
  22. package/dist/bash-subcommand-parser.js +8 -0
  23. package/dist/batch-tool-confirmation.d.ts +18 -0
  24. package/dist/batch-tool-confirmation.d.ts.map +1 -0
  25. package/dist/batch-tool-confirmation.js +100 -0
  26. package/dist/break-cache-runtime.d.ts +40 -0
  27. package/dist/break-cache-runtime.d.ts.map +1 -0
  28. package/dist/break-cache-runtime.js +17 -0
  29. package/dist/btw-runtime.d.ts +45 -0
  30. package/dist/btw-runtime.d.ts.map +1 -0
  31. package/dist/btw-runtime.js +14 -0
  32. package/dist/cache-command-runtime.d.ts +22 -0
  33. package/dist/cache-command-runtime.d.ts.map +1 -0
  34. package/dist/cache-command-runtime.js +33 -0
  35. package/dist/cache-freshness.d.ts +26 -0
  36. package/dist/cache-freshness.d.ts.map +1 -0
  37. package/dist/cache-freshness.js +14 -0
  38. package/dist/capability-runtime.d.ts +75 -0
  39. package/dist/capability-runtime.d.ts.map +1 -0
  40. package/dist/capability-runtime.js +97 -0
  41. package/dist/chunk-2FVEKTKN.js +86 -0
  42. package/dist/chunk-2YL5VKJ5.js +531 -0
  43. package/dist/chunk-3AKXDMVS.js +85 -0
  44. package/dist/chunk-3GCIXKCI.js +221 -0
  45. package/dist/chunk-3LT6OWQ2.js +258 -0
  46. package/dist/chunk-3XNUDLOM.js +91 -0
  47. package/dist/chunk-4EIC5BCT.js +475 -0
  48. package/dist/chunk-4EP3TUFF.js +238 -0
  49. package/dist/chunk-4I2NWBOM.js +117 -0
  50. package/dist/chunk-4K6UB524.js +443 -0
  51. package/dist/chunk-4T5DJW54.js +88 -0
  52. package/dist/chunk-4WHZ6JKD.js +8105 -0
  53. package/dist/chunk-5H7RRF7C.js +6 -0
  54. package/dist/chunk-64MJP6FK.js +558 -0
  55. package/dist/chunk-66BNE332.js +1617 -0
  56. package/dist/chunk-6CI6TKLN.js +50 -0
  57. package/dist/chunk-6RJVTUWI.js +16 -0
  58. package/dist/chunk-6SMM5CMP.js +100 -0
  59. package/dist/chunk-6USL33ZO.js +289 -0
  60. package/dist/chunk-7ZE5JFAZ.js +760 -0
  61. package/dist/chunk-AKPXMCVF.js +1373 -0
  62. package/dist/chunk-AO4CXI37.js +284 -0
  63. package/dist/chunk-APJIC2O5.js +150 -0
  64. package/dist/chunk-B4GQTZMT.js +607 -0
  65. package/dist/chunk-BFVPLGAC.js +93 -0
  66. package/dist/chunk-BOV4K7FP.js +588 -0
  67. package/dist/chunk-CFMSB6ST.js +59 -0
  68. package/dist/chunk-CFXB5SE5.js +502 -0
  69. package/dist/chunk-CRQORMFF.js +32 -0
  70. package/dist/chunk-CRSLDQOA.js +146 -0
  71. package/dist/chunk-CRW3636S.js +214 -0
  72. package/dist/chunk-D3UTL2QX.js +86 -0
  73. package/dist/chunk-D7TCSOAZ.js +331 -0
  74. package/dist/chunk-DDXS2RDZ.js +228 -0
  75. package/dist/chunk-DICVLZHH.js +144 -0
  76. package/dist/chunk-DNQN74CC.js +1186 -0
  77. package/dist/chunk-DTK5NBTV.js +912 -0
  78. package/dist/chunk-E72U7WW7.js +50 -0
  79. package/dist/chunk-EBACIBJP.js +356 -0
  80. package/dist/chunk-ECFLCY5V.js +530 -0
  81. package/dist/chunk-EORRZP3F.js +109 -0
  82. package/dist/chunk-F43AMWLZ.js +115 -0
  83. package/dist/chunk-F46N43MG.js +56 -0
  84. package/dist/chunk-F6TVBCZV.js +51 -0
  85. package/dist/chunk-FRIMDSRU.js +242 -0
  86. package/dist/chunk-FTAEPAVY.js +314 -0
  87. package/dist/chunk-GC55DMZV.js +156 -0
  88. package/dist/chunk-GCMH5P4W.js +422 -0
  89. package/dist/chunk-GDQQNPBP.js +251 -0
  90. package/dist/chunk-GKZYTPN2.js +279 -0
  91. package/dist/chunk-GLWTWEGX.js +272 -0
  92. package/dist/chunk-GMUCSNUD.js +586 -0
  93. package/dist/chunk-GTP2KPLY.js +167 -0
  94. package/dist/chunk-GW5YDCRZ.js +42 -0
  95. package/dist/chunk-H3T4EF5F.js +87 -0
  96. package/dist/chunk-HAEU6OTS.js +381 -0
  97. package/dist/chunk-HQIJGQYU.js +172 -0
  98. package/dist/chunk-HW2TBXVK.js +34 -0
  99. package/dist/chunk-HWD22O6F.js +94 -0
  100. package/dist/chunk-I3NTAIMF.js +280 -0
  101. package/dist/chunk-IN42UISW.js +372 -0
  102. package/dist/chunk-IWUIOLMF.js +82 -0
  103. package/dist/chunk-J7ZE2JKP.js +283 -0
  104. package/dist/chunk-JBLVBO3U.js +109 -0
  105. package/dist/chunk-JDWXC2OQ.js +800 -0
  106. package/dist/chunk-JGDIO2NF.js +74 -0
  107. package/dist/chunk-JHGWGH2C.js +182 -0
  108. package/dist/chunk-JLCPHVBN.js +42 -0
  109. package/dist/chunk-JO5ANTMR.js +6027 -0
  110. package/dist/chunk-JPXSFZFC.js +100 -0
  111. package/dist/chunk-JQCRCDOC.js +1195 -0
  112. package/dist/chunk-K4FSYBSY.js +41 -0
  113. package/dist/chunk-K56D3X7S.js +1225 -0
  114. package/dist/chunk-KI7C6ZOD.js +92 -0
  115. package/dist/chunk-KO64OFSG.js +58 -0
  116. package/dist/chunk-KQJHVJZB.js +227 -0
  117. package/dist/chunk-KTWV5JC5.js +59 -0
  118. package/dist/chunk-L4XXW6Y5.js +374 -0
  119. package/dist/chunk-L5GOCMZP.js +18 -0
  120. package/dist/chunk-LDABZV5V.js +278 -0
  121. package/dist/chunk-LHHKPGLX.js +903 -0
  122. package/dist/chunk-LL7LBRDL.js +60 -0
  123. package/dist/chunk-LQTVYUT3.js +421 -0
  124. package/dist/chunk-LVIM2PA7.js +663 -0
  125. package/dist/chunk-M4AIK2SP.js +215 -0
  126. package/dist/chunk-MLUIOBKL.js +420 -0
  127. package/dist/chunk-MOJL4X4L.js +47 -0
  128. package/dist/chunk-MVONSJEN.js +300 -0
  129. package/dist/chunk-N7G2X2LK.js +709 -0
  130. package/dist/chunk-NKFKOEYX.js +146 -0
  131. package/dist/chunk-NMNOAFFT.js +167 -0
  132. package/dist/chunk-NSIKYD3X.js +74 -0
  133. package/dist/chunk-O7EFPSMC.js +60 -0
  134. package/dist/chunk-O7S3HYE6.js +210 -0
  135. package/dist/chunk-OEYW3JE3.js +262 -0
  136. package/dist/chunk-OF3K3CU5.js +159 -0
  137. package/dist/chunk-OMZYPQVA.js +298 -0
  138. package/dist/chunk-OZMX3E35.js +248 -0
  139. package/dist/chunk-P277M7Z5.js +303 -0
  140. package/dist/chunk-P4CPRJLI.js +65 -0
  141. package/dist/chunk-PBIPV4LD.js +494 -0
  142. package/dist/chunk-PGNALDEH.js +2003 -0
  143. package/dist/chunk-PGWW3DVE.js +157 -0
  144. package/dist/chunk-QB4VWH7W.js +515 -0
  145. package/dist/chunk-QBCGAIU7.js +120 -0
  146. package/dist/chunk-QIU3FJPC.js +99 -0
  147. package/dist/chunk-QLRTQHTR.js +744 -0
  148. package/dist/chunk-QTDGRZMZ.js +171 -0
  149. package/dist/chunk-RBOQJFIZ.js +183 -0
  150. package/dist/chunk-RDGM4RUE.js +178 -0
  151. package/dist/chunk-RM5JDRCW.js +71 -0
  152. package/dist/chunk-RM5QMOFY.js +189 -0
  153. package/dist/chunk-RMLTVKOY.js +588 -0
  154. package/dist/chunk-RVY34HK4.js +79 -0
  155. package/dist/chunk-S3YWTBXL.js +43 -0
  156. package/dist/chunk-SJOS5PM6.js +64 -0
  157. package/dist/chunk-SOPYWTFV.js +90 -0
  158. package/dist/chunk-SQIBPLLV.js +15 -0
  159. package/dist/chunk-SRZIA6B4.js +136 -0
  160. package/dist/chunk-SYBNJZEL.js +250 -0
  161. package/dist/chunk-TFZEFIQT.js +17 -0
  162. package/dist/chunk-THZBFYAH.js +136 -0
  163. package/dist/chunk-TJLC3QAS.js +83 -0
  164. package/dist/chunk-TYF4GJR3.js +598 -0
  165. package/dist/chunk-UDQT5CWK.js +252 -0
  166. package/dist/chunk-UOHZQIVL.js +168 -0
  167. package/dist/chunk-URNQUECO.js +187 -0
  168. package/dist/chunk-UT2TYK2A.js +148 -0
  169. package/dist/chunk-UT5BOJKU.js +190 -0
  170. package/dist/chunk-V7GDHHBL.js +526 -0
  171. package/dist/chunk-VCSRBS47.js +133 -0
  172. package/dist/chunk-VIBENDE3.js +152 -0
  173. package/dist/chunk-VTQL23UM.js +302 -0
  174. package/dist/chunk-W3KHJNSH.js +281 -0
  175. package/dist/chunk-W6NU7ZAV.js +52 -0
  176. package/dist/chunk-WHFDDZHY.js +407 -0
  177. package/dist/chunk-WJWYRBWH.js +48 -0
  178. package/dist/chunk-WKMBK5UP.js +145 -0
  179. package/dist/chunk-WQMSRO4D.js +489 -0
  180. package/dist/chunk-WV6YJGZR.js +81 -0
  181. package/dist/chunk-WVAQYKGL.js +269 -0
  182. package/dist/chunk-X7A22JHP.js +106 -0
  183. package/dist/chunk-XFPNU42I.js +59 -0
  184. package/dist/chunk-YH6UIHNF.js +161 -0
  185. package/dist/chunk-YPQFSFYK.js +19 -0
  186. package/dist/chunk-YTQFOZRZ.js +110 -0
  187. package/dist/chunk-ZJK25VFP.js +506 -0
  188. package/dist/chunk-ZOSK2UEU.js +1555 -0
  189. package/dist/chunk-ZYSN7AWW.js +336 -0
  190. package/dist/chunk-ZZ2CNCXJ.js +282 -0
  191. package/dist/command-panel-runtime.d.ts +33 -0
  192. package/dist/command-panel-runtime.d.ts.map +1 -0
  193. package/dist/command-panel-runtime.js +80 -0
  194. package/dist/compact-cache-command-runtime.d.ts +38 -0
  195. package/dist/compact-cache-command-runtime.d.ts.map +1 -0
  196. package/dist/compact-cache-command-runtime.js +146 -0
  197. package/dist/compact-context.d.ts +35 -0
  198. package/dist/compact-context.d.ts.map +1 -0
  199. package/dist/compact-context.js +16 -0
  200. package/dist/compact-preflight-runtime.d.ts +46 -0
  201. package/dist/compact-preflight-runtime.d.ts.map +1 -0
  202. package/dist/compact-preflight-runtime.js +62 -0
  203. package/dist/connector-runtime.d.ts +72 -0
  204. package/dist/connector-runtime.d.ts.map +1 -0
  205. package/dist/connector-runtime.js +88 -0
  206. package/dist/context-estimator.d.ts +20 -0
  207. package/dist/context-estimator.d.ts.map +1 -0
  208. package/dist/context-estimator.js +18 -0
  209. package/dist/context-window-runtime.d.ts +12 -0
  210. package/dist/context-window-runtime.d.ts.map +1 -0
  211. package/dist/context-window-runtime.js +10 -0
  212. package/dist/deep-compact-runtime.d.ts +53 -0
  213. package/dist/deep-compact-runtime.d.ts.map +1 -0
  214. package/dist/deep-compact-runtime.js +35 -0
  215. package/dist/deferred-tools-catalog.d.ts +52 -0
  216. package/dist/deferred-tools-catalog.d.ts.map +1 -0
  217. package/dist/deferred-tools-catalog.js +42 -0
  218. package/dist/details-status-runtime.d.ts +48 -0
  219. package/dist/details-status-runtime.d.ts.map +1 -0
  220. package/dist/details-status-runtime.js +102 -0
  221. package/dist/evidence-runtime.d.ts +48 -0
  222. package/dist/evidence-runtime.d.ts.map +1 -0
  223. package/dist/evidence-runtime.js +92 -0
  224. package/dist/extension-command-runtime.d.ts +63 -0
  225. package/dist/extension-command-runtime.d.ts.map +1 -0
  226. package/dist/extension-command-runtime.js +67 -0
  227. package/dist/extension-slash-runtime.d.ts +11 -0
  228. package/dist/extension-slash-runtime.d.ts.map +1 -0
  229. package/dist/extension-slash-runtime.js +79 -0
  230. package/dist/external-editor-runtime.d.ts +32 -0
  231. package/dist/external-editor-runtime.d.ts.map +1 -0
  232. package/dist/external-editor-runtime.js +71 -0
  233. package/dist/failure-learning-command-runtime.d.ts +10 -0
  234. package/dist/failure-learning-command-runtime.d.ts.map +1 -0
  235. package/dist/failure-learning-command-runtime.js +78 -0
  236. package/dist/failure-learning-presenter.d.ts +6 -0
  237. package/dist/failure-learning-presenter.d.ts.map +1 -0
  238. package/dist/failure-learning-presenter.js +12 -0
  239. package/dist/failure-learning-runtime.d.ts +39 -0
  240. package/dist/failure-learning-runtime.d.ts.map +1 -0
  241. package/dist/failure-learning-runtime.js +39 -0
  242. package/dist/feature-flag-runtime.d.ts +8 -0
  243. package/dist/feature-flag-runtime.d.ts.map +1 -0
  244. package/dist/feature-flag-runtime.js +12 -0
  245. package/dist/feishu-long-connection-runtime.d.ts +12 -0
  246. package/dist/feishu-long-connection-runtime.d.ts.map +1 -0
  247. package/dist/feishu-long-connection-runtime.js +7 -0
  248. package/dist/final-answer-gate.d.ts +24 -0
  249. package/dist/final-answer-gate.d.ts.map +1 -0
  250. package/dist/final-answer-gate.js +26 -0
  251. package/dist/ghost-text.d.ts +16 -0
  252. package/dist/ghost-text.d.ts.map +1 -0
  253. package/dist/ghost-text.js +8 -0
  254. package/dist/git-branch-runtime.d.ts +14 -0
  255. package/dist/git-branch-runtime.d.ts.map +1 -0
  256. package/dist/git-branch-runtime.js +68 -0
  257. package/dist/git-command-runtime.d.ts +22 -0
  258. package/dist/git-command-runtime.d.ts.map +1 -0
  259. package/dist/git-command-runtime.js +89 -0
  260. package/dist/git-operation-runtime.d.ts +188 -0
  261. package/dist/git-operation-runtime.d.ts.map +1 -0
  262. package/dist/git-operation-runtime.js +45 -0
  263. package/dist/git-runtime.d.ts +113 -0
  264. package/dist/git-runtime.d.ts.map +1 -0
  265. package/dist/git-runtime.js +16 -0
  266. package/dist/git-slash-runtime.d.ts +38 -0
  267. package/dist/git-slash-runtime.d.ts.map +1 -0
  268. package/dist/git-slash-runtime.js +18 -0
  269. package/dist/git-tool-dispatch-runtime.d.ts +108 -0
  270. package/dist/git-tool-dispatch-runtime.d.ts.map +1 -0
  271. package/dist/git-tool-dispatch-runtime.js +25 -0
  272. package/dist/git-tool-runtime.d.ts +64 -0
  273. package/dist/git-tool-runtime.d.ts.map +1 -0
  274. package/dist/git-tool-runtime.js +36 -0
  275. package/dist/guard-wiring.d.ts +67 -0
  276. package/dist/guard-wiring.d.ts.map +1 -0
  277. package/dist/guard-wiring.js +21 -0
  278. package/dist/handoff-session-runtime.d.ts +11 -0
  279. package/dist/handoff-session-runtime.d.ts.map +1 -0
  280. package/dist/handoff-session-runtime.js +36 -0
  281. package/dist/index-result-presenter.d.ts +14 -0
  282. package/dist/index-result-presenter.d.ts.map +1 -0
  283. package/dist/index-result-presenter.js +28 -0
  284. package/dist/index-runtime.d.ts +64 -0
  285. package/dist/index-runtime.d.ts.map +1 -0
  286. package/dist/index-runtime.js +28 -0
  287. package/dist/index-tool-runtime.d.ts +44 -0
  288. package/dist/index-tool-runtime.d.ts.map +1 -0
  289. package/dist/index-tool-runtime.js +24 -0
  290. package/dist/index.d.ts +57 -0
  291. package/dist/index.d.ts.map +1 -0
  292. package/dist/index.js +2346 -0
  293. package/dist/job-agent-command-runtime.d.ts +127 -0
  294. package/dist/job-agent-command-runtime.d.ts.map +1 -0
  295. package/dist/job-agent-command-runtime.js +138 -0
  296. package/dist/job-runner-presenter.d.ts +32 -0
  297. package/dist/job-runner-presenter.d.ts.map +1 -0
  298. package/dist/job-runner-presenter.js +30 -0
  299. package/dist/job-runtime.d.ts +65 -0
  300. package/dist/job-runtime.d.ts.map +1 -0
  301. package/dist/job-runtime.js +88 -0
  302. package/dist/keybinding-runtime.d.ts +29 -0
  303. package/dist/keybinding-runtime.d.ts.map +1 -0
  304. package/dist/keybinding-runtime.js +14 -0
  305. package/dist/log-artifact.d.ts +55 -0
  306. package/dist/log-artifact.d.ts.map +1 -0
  307. package/dist/log-artifact.js +10 -0
  308. package/dist/mcp-index-command-runtime.d.ts +21 -0
  309. package/dist/mcp-index-command-runtime.d.ts.map +1 -0
  310. package/dist/mcp-index-command-runtime.js +18 -0
  311. package/dist/mcp-index-runtime.d.ts +106 -0
  312. package/dist/mcp-index-runtime.d.ts.map +1 -0
  313. package/dist/mcp-index-runtime.js +142 -0
  314. package/dist/mcp-sse-runtime.d.ts +13 -0
  315. package/dist/mcp-sse-runtime.d.ts.map +1 -0
  316. package/dist/mcp-sse-runtime.js +8 -0
  317. package/dist/mcp-stdio-runtime.d.ts +37 -0
  318. package/dist/mcp-stdio-runtime.d.ts.map +1 -0
  319. package/dist/mcp-stdio-runtime.js +15 -0
  320. package/dist/memory-command-runtime.d.ts +37 -0
  321. package/dist/memory-command-runtime.d.ts.map +1 -0
  322. package/dist/memory-command-runtime.js +88 -0
  323. package/dist/memory-eviction-runtime.d.ts +7 -0
  324. package/dist/memory-eviction-runtime.d.ts.map +1 -0
  325. package/dist/memory-eviction-runtime.js +12 -0
  326. package/dist/memory-extraction-runtime.d.ts +40 -0
  327. package/dist/memory-extraction-runtime.d.ts.map +1 -0
  328. package/dist/memory-extraction-runtime.js +20 -0
  329. package/dist/memory-rules-runtime.d.ts +15 -0
  330. package/dist/memory-rules-runtime.d.ts.map +1 -0
  331. package/dist/memory-rules-runtime.js +14 -0
  332. package/dist/meta-scheduler-complexity.d.ts +16 -0
  333. package/dist/meta-scheduler-complexity.d.ts.map +1 -0
  334. package/dist/meta-scheduler-complexity.js +37 -0
  335. package/dist/meta-scheduler-runtime.d.ts +288 -0
  336. package/dist/meta-scheduler-runtime.d.ts.map +1 -0
  337. package/dist/meta-scheduler-runtime.js +17 -0
  338. package/dist/model-command-runtime.d.ts +12 -0
  339. package/dist/model-command-runtime.d.ts.map +1 -0
  340. package/dist/model-command-runtime.js +84 -0
  341. package/dist/model-doctor-runtime.d.ts +81 -0
  342. package/dist/model-doctor-runtime.d.ts.map +1 -0
  343. package/dist/model-doctor-runtime.js +46 -0
  344. package/dist/model-loop-runtime.d.ts +189 -0
  345. package/dist/model-loop-runtime.d.ts.map +1 -0
  346. package/dist/model-loop-runtime.js +141 -0
  347. package/dist/model-prompt-runtime.d.ts +25 -0
  348. package/dist/model-prompt-runtime.d.ts.map +1 -0
  349. package/dist/model-prompt-runtime.js +33 -0
  350. package/dist/model-setup-runtime.d.ts +28 -0
  351. package/dist/model-setup-runtime.d.ts.map +1 -0
  352. package/dist/model-setup-runtime.js +26 -0
  353. package/dist/model-stream-runtime.d.ts +47 -0
  354. package/dist/model-stream-runtime.d.ts.map +1 -0
  355. package/dist/model-stream-runtime.js +136 -0
  356. package/dist/model-tool-runtime.d.ts +114 -0
  357. package/dist/model-tool-runtime.d.ts.map +1 -0
  358. package/dist/model-tool-runtime.js +144 -0
  359. package/dist/natural-command-bridge.d.ts +158 -0
  360. package/dist/natural-command-bridge.d.ts.map +1 -0
  361. package/dist/natural-command-bridge.js +36 -0
  362. package/dist/pending-details-presenter.d.ts +8 -0
  363. package/dist/pending-details-presenter.d.ts.map +1 -0
  364. package/dist/pending-details-presenter.js +12 -0
  365. package/dist/permission-approval-runtime.d.ts +28 -0
  366. package/dist/permission-approval-runtime.d.ts.map +1 -0
  367. package/dist/permission-approval-runtime.js +136 -0
  368. package/dist/permission-continuation-runtime.d.ts +90 -0
  369. package/dist/permission-continuation-runtime.d.ts.map +1 -0
  370. package/dist/permission-continuation-runtime.js +68 -0
  371. package/dist/permission-policy-engine.d.ts +51 -0
  372. package/dist/permission-policy-engine.d.ts.map +1 -0
  373. package/dist/permission-policy-engine.js +12 -0
  374. package/dist/permission-presenter.d.ts +13 -0
  375. package/dist/permission-presenter.d.ts.map +1 -0
  376. package/dist/permission-presenter.js +8 -0
  377. package/dist/persistent-history.d.ts +18 -0
  378. package/dist/persistent-history.d.ts.map +1 -0
  379. package/dist/persistent-history.js +126 -0
  380. package/dist/platform-security.d.ts +4 -0
  381. package/dist/platform-security.d.ts.map +1 -0
  382. package/dist/platform-security.js +10 -0
  383. package/dist/process-command-runtime.d.ts +9 -0
  384. package/dist/process-command-runtime.d.ts.map +1 -0
  385. package/dist/process-command-runtime.js +11 -0
  386. package/dist/process-guard.d.ts +56 -0
  387. package/dist/process-guard.d.ts.map +1 -0
  388. package/dist/process-guard.js +22 -0
  389. package/dist/prompt-command-runtime.d.ts +12 -0
  390. package/dist/prompt-command-runtime.d.ts.map +1 -0
  391. package/dist/prompt-command-runtime.js +10 -0
  392. package/dist/prompt-stash.d.ts +15 -0
  393. package/dist/prompt-stash.d.ts.map +1 -0
  394. package/dist/prompt-stash.js +8 -0
  395. package/dist/provider-circuit-breaker.d.ts +132 -0
  396. package/dist/provider-circuit-breaker.d.ts.map +1 -0
  397. package/dist/provider-circuit-breaker.js +31 -0
  398. package/dist/provider-loop-runtime.d.ts +18 -0
  399. package/dist/provider-loop-runtime.d.ts.map +1 -0
  400. package/dist/provider-loop-runtime.js +16 -0
  401. package/dist/remote-command-runtime.d.ts +47 -0
  402. package/dist/remote-command-runtime.d.ts.map +1 -0
  403. package/dist/remote-command-runtime.js +125 -0
  404. package/dist/remote-inbound-bridge-runtime.d.ts +152 -0
  405. package/dist/remote-inbound-bridge-runtime.d.ts.map +1 -0
  406. package/dist/remote-inbound-bridge-runtime.js +44 -0
  407. package/dist/remote-mcp-presenter.d.ts +5 -0
  408. package/dist/remote-mcp-presenter.d.ts.map +1 -0
  409. package/dist/remote-mcp-presenter.js +10 -0
  410. package/dist/remote-repl-bridge-runtime.d.ts +134 -0
  411. package/dist/remote-repl-bridge-runtime.d.ts.map +1 -0
  412. package/dist/remote-repl-bridge-runtime.js +20 -0
  413. package/dist/remote-transport.d.ts +50 -0
  414. package/dist/remote-transport.d.ts.map +1 -0
  415. package/dist/remote-transport.js +14 -0
  416. package/dist/request-lifecycle-presenter.d.ts +25 -0
  417. package/dist/request-lifecycle-presenter.d.ts.map +1 -0
  418. package/dist/request-lifecycle-presenter.js +22 -0
  419. package/dist/runner-runtime.d.ts +41 -0
  420. package/dist/runner-runtime.d.ts.map +1 -0
  421. package/dist/runner-runtime.js +35 -0
  422. package/dist/runtime-budget.d.ts +38 -0
  423. package/dist/runtime-budget.d.ts.map +1 -0
  424. package/dist/runtime-budget.js +78 -0
  425. package/dist/runtime-path-marker.d.ts +112 -0
  426. package/dist/runtime-path-marker.d.ts.map +1 -0
  427. package/dist/runtime-path-marker.js +18 -0
  428. package/dist/runtime-status-presenter.d.ts +22 -0
  429. package/dist/runtime-status-presenter.d.ts.map +1 -0
  430. package/dist/runtime-status-presenter.js +15 -0
  431. package/dist/runtime-status-snapshot.d.ts +52 -0
  432. package/dist/runtime-status-snapshot.d.ts.map +1 -0
  433. package/dist/runtime-status-snapshot.js +11 -0
  434. package/dist/runtime-utils.d.ts +2 -0
  435. package/dist/runtime-utils.d.ts.map +1 -0
  436. package/dist/runtime-utils.js +6 -0
  437. package/dist/shell/clipboard.d.ts +23 -0
  438. package/dist/shell/clipboard.d.ts.map +1 -0
  439. package/dist/shell/clipboard.js +8 -0
  440. package/dist/shell/components/AgentProgressTree.d.ts +9 -0
  441. package/dist/shell/components/AgentProgressTree.d.ts.map +1 -0
  442. package/dist/shell/components/AgentProgressTree.js +9 -0
  443. package/dist/shell/components/BackgroundTaskOverlay.d.ts +8 -0
  444. package/dist/shell/components/BackgroundTaskOverlay.d.ts.map +1 -0
  445. package/dist/shell/components/BackgroundTaskOverlay.js +8 -0
  446. package/dist/shell/components/BtwPanel.d.ts +16 -0
  447. package/dist/shell/components/BtwPanel.d.ts.map +1 -0
  448. package/dist/shell/components/BtwPanel.js +8 -0
  449. package/dist/shell/components/CommandPanel.d.ts +11 -0
  450. package/dist/shell/components/CommandPanel.d.ts.map +1 -0
  451. package/dist/shell/components/CommandPanel.js +8 -0
  452. package/dist/shell/components/Composer.d.ts +187 -0
  453. package/dist/shell/components/Composer.d.ts.map +1 -0
  454. package/dist/shell/components/Composer.js +99 -0
  455. package/dist/shell/components/ConfigPanel.d.ts +11 -0
  456. package/dist/shell/components/ConfigPanel.d.ts.map +1 -0
  457. package/dist/shell/components/ConfigPanel.js +8 -0
  458. package/dist/shell/components/CtrlOToExpand.d.ts +30 -0
  459. package/dist/shell/components/CtrlOToExpand.d.ts.map +1 -0
  460. package/dist/shell/components/CtrlOToExpand.js +16 -0
  461. package/dist/shell/components/HelpPanel.d.ts +19 -0
  462. package/dist/shell/components/HelpPanel.d.ts.map +1 -0
  463. package/dist/shell/components/HelpPanel.js +8 -0
  464. package/dist/shell/components/HistorySearchPanel.d.ts +14 -0
  465. package/dist/shell/components/HistorySearchPanel.d.ts.map +1 -0
  466. package/dist/shell/components/HistorySearchPanel.js +104 -0
  467. package/dist/shell/components/MessageMarkdown.d.ts +34 -0
  468. package/dist/shell/components/MessageMarkdown.d.ts.map +1 -0
  469. package/dist/shell/components/MessageMarkdown.js +18 -0
  470. package/dist/shell/components/MessageResponse.d.ts +14 -0
  471. package/dist/shell/components/MessageResponse.d.ts.map +1 -0
  472. package/dist/shell/components/MessageResponse.js +6 -0
  473. package/dist/shell/components/MouseInputRouter.d.ts +28 -0
  474. package/dist/shell/components/MouseInputRouter.d.ts.map +1 -0
  475. package/dist/shell/components/MouseInputRouter.js +84 -0
  476. package/dist/shell/components/NotificationStack.d.ts +19 -0
  477. package/dist/shell/components/NotificationStack.d.ts.map +1 -0
  478. package/dist/shell/components/NotificationStack.js +6 -0
  479. package/dist/shell/components/OutputLine.d.ts +19 -0
  480. package/dist/shell/components/OutputLine.d.ts.map +1 -0
  481. package/dist/shell/components/OutputLine.js +38 -0
  482. package/dist/shell/components/ProductBlock.d.ts +10 -0
  483. package/dist/shell/components/ProductBlock.d.ts.map +1 -0
  484. package/dist/shell/components/ProductBlock.js +12 -0
  485. package/dist/shell/components/ScrollViewport.d.ts +43 -0
  486. package/dist/shell/components/ScrollViewport.d.ts.map +1 -0
  487. package/dist/shell/components/ScrollViewport.js +86 -0
  488. package/dist/shell/components/SessionsPanel.d.ts +24 -0
  489. package/dist/shell/components/SessionsPanel.d.ts.map +1 -0
  490. package/dist/shell/components/SessionsPanel.js +8 -0
  491. package/dist/shell/components/ShellApp.d.ts +8 -0
  492. package/dist/shell/components/ShellApp.d.ts.map +1 -0
  493. package/dist/shell/components/ShellApp.js +45 -0
  494. package/dist/shell/components/ShortcutPanel.d.ts +14 -0
  495. package/dist/shell/components/ShortcutPanel.d.ts.map +1 -0
  496. package/dist/shell/components/ShortcutPanel.js +59 -0
  497. package/dist/shell/components/SlashSuggestions.d.ts +24 -0
  498. package/dist/shell/components/SlashSuggestions.d.ts.map +1 -0
  499. package/dist/shell/components/SlashSuggestions.js +7 -0
  500. package/dist/shell/components/StatusFooter.d.ts +30 -0
  501. package/dist/shell/components/StatusFooter.d.ts.map +1 -0
  502. package/dist/shell/components/StatusFooter.js +7 -0
  503. package/dist/shell/components/StatusTray.d.ts +9 -0
  504. package/dist/shell/components/StatusTray.d.ts.map +1 -0
  505. package/dist/shell/components/StatusTray.js +6 -0
  506. package/dist/shell/components/StructuredDiff.d.ts +9 -0
  507. package/dist/shell/components/StructuredDiff.d.ts.map +1 -0
  508. package/dist/shell/components/StructuredDiff.js +7 -0
  509. package/dist/shell/components/TaskListView.d.ts +9 -0
  510. package/dist/shell/components/TaskListView.d.ts.map +1 -0
  511. package/dist/shell/components/TaskListView.js +9 -0
  512. package/dist/shell/components/TaskSuggestionBar.d.ts +15 -0
  513. package/dist/shell/components/TaskSuggestionBar.d.ts.map +1 -0
  514. package/dist/shell/components/TaskSuggestionBar.js +8 -0
  515. package/dist/shell/components/UnseenMessagePill.d.ts +12 -0
  516. package/dist/shell/components/UnseenMessagePill.d.ts.map +1 -0
  517. package/dist/shell/components/UnseenMessagePill.js +6 -0
  518. package/dist/shell/components/WorkflowProgressView.d.ts +9 -0
  519. package/dist/shell/components/WorkflowProgressView.d.ts.map +1 -0
  520. package/dist/shell/components/WorkflowProgressView.js +9 -0
  521. package/dist/shell/components/useAnchoredCursor.d.ts +32 -0
  522. package/dist/shell/components/useAnchoredCursor.d.ts.map +1 -0
  523. package/dist/shell/components/useAnchoredCursor.js +6 -0
  524. package/dist/shell/hooks/useRenderThrottle.d.ts +2 -0
  525. package/dist/shell/hooks/useRenderThrottle.d.ts.map +1 -0
  526. package/dist/shell/hooks/useRenderThrottle.js +35 -0
  527. package/dist/shell/hooks/useScrollBatcher.d.ts +10 -0
  528. package/dist/shell/hooks/useScrollBatcher.d.ts.map +1 -0
  529. package/dist/shell/hooks/useScrollBatcher.js +23 -0
  530. package/dist/shell/hooks/useScrollRuntime.d.ts +2 -0
  531. package/dist/shell/hooks/useScrollRuntime.d.ts.map +1 -0
  532. package/dist/shell/hooks/useScrollRuntime.js +6 -0
  533. package/dist/shell/hyperlink-utils.d.ts +18 -0
  534. package/dist/shell/hyperlink-utils.d.ts.map +1 -0
  535. package/dist/shell/hyperlink-utils.js +8 -0
  536. package/dist/shell/ink-renderer.d.ts +18 -0
  537. package/dist/shell/ink-renderer.d.ts.map +1 -0
  538. package/dist/shell/ink-renderer.js +54 -0
  539. package/dist/shell/models/command-transcript-presenter.d.ts +73 -0
  540. package/dist/shell/models/command-transcript-presenter.d.ts.map +1 -0
  541. package/dist/shell/models/command-transcript-presenter.js +26 -0
  542. package/dist/shell/models/help-panel.d.ts +37 -0
  543. package/dist/shell/models/help-panel.d.ts.map +1 -0
  544. package/dist/shell/models/help-panel.js +10 -0
  545. package/dist/shell/models/permission-elevation.d.ts +65 -0
  546. package/dist/shell/models/permission-elevation.d.ts.map +1 -0
  547. package/dist/shell/models/permission-elevation.js +10 -0
  548. package/dist/shell/models/permission-explanation.d.ts +45 -0
  549. package/dist/shell/models/permission-explanation.d.ts.map +1 -0
  550. package/dist/shell/models/permission-explanation.js +14 -0
  551. package/dist/shell/models/session-panel.d.ts +22 -0
  552. package/dist/shell/models/session-panel.d.ts.map +1 -0
  553. package/dist/shell/models/session-panel.js +18 -0
  554. package/dist/shell/models/task-scroll-state.d.ts +24 -0
  555. package/dist/shell/models/task-scroll-state.d.ts.map +1 -0
  556. package/dist/shell/models/task-scroll-state.js +26 -0
  557. package/dist/shell/models/terminal-input-runtime.d.ts +37 -0
  558. package/dist/shell/models/terminal-input-runtime.d.ts.map +1 -0
  559. package/dist/shell/models/terminal-input-runtime.js +18 -0
  560. package/dist/shell/models/transcript-scroll-state.d.ts +57 -0
  561. package/dist/shell/models/transcript-scroll-state.d.ts.map +1 -0
  562. package/dist/shell/models/transcript-scroll-state.js +12 -0
  563. package/dist/shell/models/transcript-selection-state.d.ts +39 -0
  564. package/dist/shell/models/transcript-selection-state.d.ts.map +1 -0
  565. package/dist/shell/models/transcript-selection-state.js +25 -0
  566. package/dist/shell/models/wheel-acceleration.d.ts +52 -0
  567. package/dist/shell/models/wheel-acceleration.d.ts.map +1 -0
  568. package/dist/shell/models/wheel-acceleration.js +6 -0
  569. package/dist/shell/output-utils.d.ts +22 -0
  570. package/dist/shell/output-utils.d.ts.map +1 -0
  571. package/dist/shell/output-utils.js +12 -0
  572. package/dist/shell/plain-renderer.d.ts +12 -0
  573. package/dist/shell/plain-renderer.d.ts.map +1 -0
  574. package/dist/shell/plain-renderer.js +13 -0
  575. package/dist/shell/progress-views.d.ts +10 -0
  576. package/dist/shell/progress-views.d.ts.map +1 -0
  577. package/dist/shell/progress-views.js +17 -0
  578. package/dist/shell/stdout-flush-barrier.d.ts +39 -0
  579. package/dist/shell/stdout-flush-barrier.d.ts.map +1 -0
  580. package/dist/shell/stdout-flush-barrier.js +10 -0
  581. package/dist/shell/terminal-capability.d.ts +54 -0
  582. package/dist/shell/terminal-capability.d.ts.map +1 -0
  583. package/dist/shell/terminal-capability.js +10 -0
  584. package/dist/shell/terminal-interaction-runtime.d.ts +11 -0
  585. package/dist/shell/terminal-interaction-runtime.d.ts.map +1 -0
  586. package/dist/shell/terminal-interaction-runtime.js +38 -0
  587. package/dist/shell/terminal-state-recovery.d.ts +52 -0
  588. package/dist/shell/terminal-state-recovery.d.ts.map +1 -0
  589. package/dist/shell/terminal-state-recovery.js +11 -0
  590. package/dist/shell/text-utils.d.ts +40 -0
  591. package/dist/shell/text-utils.d.ts.map +1 -0
  592. package/dist/shell/text-utils.js +26 -0
  593. package/dist/shell/theme.d.ts +41 -0
  594. package/dist/shell/theme.d.ts.map +1 -0
  595. package/dist/shell/theme.js +8 -0
  596. package/dist/shell/types.d.ts +784 -0
  597. package/dist/shell/types.d.ts.map +1 -0
  598. package/dist/shell/types.js +0 -0
  599. package/dist/shell/view-model.d.ts +82 -0
  600. package/dist/shell/view-model.d.ts.map +1 -0
  601. package/dist/shell/view-model.js +48 -0
  602. package/dist/slash-command-runtime.d.ts +64 -0
  603. package/dist/slash-command-runtime.d.ts.map +1 -0
  604. package/dist/slash-command-runtime.js +186 -0
  605. package/dist/slash-dispatch.d.ts +48 -0
  606. package/dist/slash-dispatch.d.ts.map +1 -0
  607. package/dist/slash-dispatch.js +53 -0
  608. package/dist/startup-runtime.d.ts +48 -0
  609. package/dist/startup-runtime.d.ts.map +1 -0
  610. package/dist/startup-runtime.js +43 -0
  611. package/dist/terminal-readiness-presenter.d.ts +131 -0
  612. package/dist/terminal-readiness-presenter.d.ts.map +1 -0
  613. package/dist/terminal-readiness-presenter.js +16 -0
  614. package/dist/terminal-readiness-runtime.d.ts +5 -0
  615. package/dist/terminal-readiness-runtime.d.ts.map +1 -0
  616. package/dist/terminal-readiness-runtime.js +15 -0
  617. package/dist/terminal-setup-runtime.d.ts +34 -0
  618. package/dist/terminal-setup-runtime.d.ts.map +1 -0
  619. package/dist/terminal-setup-runtime.js +257 -0
  620. package/dist/tool-output-presenter.d.ts +38 -0
  621. package/dist/tool-output-presenter.d.ts.map +1 -0
  622. package/dist/tool-output-presenter.js +16 -0
  623. package/dist/tool-result-budget.d.ts +42 -0
  624. package/dist/tool-result-budget.d.ts.map +1 -0
  625. package/dist/tool-result-budget.js +11 -0
  626. package/dist/tui-agent-job-runtime.d.ts +38 -0
  627. package/dist/tui-agent-job-runtime.d.ts.map +1 -0
  628. package/dist/tui-agent-job-runtime.js +86 -0
  629. package/dist/tui-context-runtime.d.ts +426 -0
  630. package/dist/tui-context-runtime.d.ts.map +1 -0
  631. package/dist/tui-context-runtime.js +65 -0
  632. package/dist/tui-data-types.d.ts +923 -0
  633. package/dist/tui-data-types.d.ts.map +1 -0
  634. package/dist/tui-data-types.js +0 -0
  635. package/dist/tui-details-runtime.d.ts +25 -0
  636. package/dist/tui-details-runtime.d.ts.map +1 -0
  637. package/dist/tui-details-runtime.js +29 -0
  638. package/dist/tui-memory-runtime.d.ts +33 -0
  639. package/dist/tui-memory-runtime.d.ts.map +1 -0
  640. package/dist/tui-memory-runtime.js +58 -0
  641. package/dist/tui-messages.d.ts +4 -0
  642. package/dist/tui-messages.d.ts.map +1 -0
  643. package/dist/tui-messages.js +6 -0
  644. package/dist/tui-model-runtime.d.ts +30 -0
  645. package/dist/tui-model-runtime.d.ts.map +1 -0
  646. package/dist/tui-model-runtime.js +39 -0
  647. package/dist/tui-output-surface.d.ts +141 -0
  648. package/dist/tui-output-surface.d.ts.map +1 -0
  649. package/dist/tui-output-surface.js +55 -0
  650. package/dist/tui-permission-runtime.d.ts +62 -0
  651. package/dist/tui-permission-runtime.d.ts.map +1 -0
  652. package/dist/tui-permission-runtime.js +24 -0
  653. package/dist/tui-state-runtime.d.ts +21 -0
  654. package/dist/tui-state-runtime.d.ts.map +1 -0
  655. package/dist/tui-state-runtime.js +47 -0
  656. package/dist/turn-continuity-runtime.d.ts +30 -0
  657. package/dist/turn-continuity-runtime.d.ts.map +1 -0
  658. package/dist/turn-continuity-runtime.js +8 -0
  659. package/dist/undo-ring.d.ts +16 -0
  660. package/dist/undo-ring.d.ts.map +1 -0
  661. package/dist/undo-ring.js +12 -0
  662. package/dist/usage-stats-presenter.d.ts +17 -0
  663. package/dist/usage-stats-presenter.d.ts.map +1 -0
  664. package/dist/usage-stats-presenter.js +22 -0
  665. package/dist/user-state-signal-runtime.d.ts +33 -0
  666. package/dist/user-state-signal-runtime.d.ts.map +1 -0
  667. package/dist/user-state-signal-runtime.js +8 -0
  668. package/dist/verification-command-runtime.d.ts +25 -0
  669. package/dist/verification-command-runtime.d.ts.map +1 -0
  670. package/dist/verification-command-runtime.js +51 -0
  671. package/dist/verification-level.d.ts +93 -0
  672. package/dist/verification-level.d.ts.map +1 -0
  673. package/dist/verification-level.js +18 -0
  674. package/dist/workflow-agent-runtime-bridge.d.ts +169 -0
  675. package/dist/workflow-agent-runtime-bridge.d.ts.map +1 -0
  676. package/dist/workflow-agent-runtime-bridge.js +9 -0
  677. package/dist/workflow-command-runtime.d.ts +94 -0
  678. package/dist/workflow-command-runtime.d.ts.map +1 -0
  679. package/dist/workflow-command-runtime.js +114 -0
  680. package/dist/workflow-plan-schema.d.ts +161 -0
  681. package/dist/workflow-plan-schema.d.ts.map +1 -0
  682. package/dist/workflow-plan-schema.js +12 -0
  683. package/dist/workflow-planner-entry.d.ts +52 -0
  684. package/dist/workflow-planner-entry.d.ts.map +1 -0
  685. package/dist/workflow-planner-entry.js +328 -0
  686. package/dist/workflow-task-surface.d.ts +39 -0
  687. package/dist/workflow-task-surface.d.ts.map +1 -0
  688. package/dist/workflow-task-surface.js +7 -0
  689. package/dist/workspace-reference-cache.d.ts +110 -0
  690. package/dist/workspace-reference-cache.d.ts.map +1 -0
  691. package/dist/workspace-reference-cache.js +12 -0
  692. package/package.json +41 -0
@@ -0,0 +1,1617 @@
1
+ import {
2
+ buildOfficialCliInvocation,
3
+ buildWebhookRequest,
4
+ defaultRemoteTransportDeps,
5
+ deliverOfficialCli,
6
+ deliverWebhook
7
+ } from "./chunk-UT5BOJKU.js";
8
+ import {
9
+ formatRemoteStatus,
10
+ formatRemoteTestResult
11
+ } from "./chunk-W6NU7ZAV.js";
12
+ import {
13
+ startFeishuLongConnection
14
+ } from "./chunk-WJWYRBWH.js";
15
+ import {
16
+ cancelRemotePairing,
17
+ clearRemoteInbox,
18
+ createRemotePairing,
19
+ createSignedRemoteInboundFixture,
20
+ drainRemoteInbox,
21
+ formatRemoteBridgeDoctor,
22
+ formatRemoteInbox,
23
+ formatRemotePairing,
24
+ formatRemotePairingStatus,
25
+ getRemoteBridgeDoctor,
26
+ rejectRemoteInboxItem
27
+ } from "./chunk-CFXB5SE5.js";
28
+ import {
29
+ showCommandPanel
30
+ } from "./chunk-JO5ANTMR.js";
31
+ import {
32
+ redactRemoteSummary,
33
+ remoteTranscriptSummary
34
+ } from "./chunk-J7ZE2JKP.js";
35
+ import {
36
+ applyRemoteSessionDisables,
37
+ createRemoteState
38
+ } from "./chunk-QLRTQHTR.js";
39
+ import {
40
+ createDefaultReplBridgeSocketPath,
41
+ createReplBridgeState,
42
+ handleReplBridgeMessage,
43
+ maybeRefreshJwtToken,
44
+ startReplBridgeSocketServer
45
+ } from "./chunk-4EP3TUFF.js";
46
+ import {
47
+ truncateDisplay,
48
+ writeLine
49
+ } from "./chunk-OMZYPQVA.js";
50
+
51
+ // src/remote-command-runtime.ts
52
+ import { randomUUID } from "crypto";
53
+ var runtimeDeps;
54
+ var remoteTransportDeps;
55
+ var feishuLongConnectionHandles = /* @__PURE__ */ new Map();
56
+ function configureRemoteCommandRuntime(deps2) {
57
+ runtimeDeps = deps2;
58
+ }
59
+ function configureRemoteTransport(deps2) {
60
+ remoteTransportDeps = deps2;
61
+ }
62
+ function deps() {
63
+ if (!runtimeDeps) {
64
+ throw new Error("remote-command-runtime deps not configured");
65
+ }
66
+ return runtimeDeps;
67
+ }
68
+ function refreshRemoteState(context) {
69
+ const previous = context.remote;
70
+ context.remote = createRemoteState(context.config);
71
+ context.remote.events = previous.events;
72
+ context.remote.processedMessageIds = previous.processedMessageIds;
73
+ context.remote.sessionDisabledChannelIds = previous.sessionDisabledChannelIds;
74
+ context.remote.localReplBridge = previous.localReplBridge;
75
+ context.remote.localReplBridgeSocket = previous.localReplBridgeSocket;
76
+ context.remote.lastDoctor = previous.lastDoctor;
77
+ context.remote.lastApproval = previous.lastApproval;
78
+ applyRemoteSessionDisables(context.remote);
79
+ }
80
+ async function handleRemoteCommand(args, context, output) {
81
+ refreshRemoteState(context);
82
+ const action = args[0] ?? "status";
83
+ if (action === "status") {
84
+ const isEn = context.language === "en-US";
85
+ const enabled = context.remote.enabled;
86
+ const lastDoctor = context.remote.lastDoctor;
87
+ const summary = [
88
+ isEn ? `Remote ${enabled ? "enabled" : "disabled"}` : `\u8FDC\u7A0B ${enabled ? "\u5DF2\u542F\u7528" : "\u672A\u542F\u7528"}`
89
+ ];
90
+ if (!lastDoctor) {
91
+ summary.push(
92
+ isEn ? "Not yet diagnosed \u2014 run /remote doctor." : "\u5C1A\u672A\u8BCA\u65AD \u2014 \u8FD0\u884C /remote doctor\u3002"
93
+ );
94
+ }
95
+ showCommandPanel(context, output, {
96
+ title: "/remote",
97
+ tone: "neutral",
98
+ summary,
99
+ actions: ["/remote doctor"],
100
+ detailsText: formatRemoteStatus(context.remote)
101
+ });
102
+ return;
103
+ }
104
+ if (action === "doctor") {
105
+ const report = formatRemoteDoctor(context);
106
+ context.remote.lastDoctor = report;
107
+ await appendRemoteSystemEvent(
108
+ context,
109
+ `remote_doctor ${remoteTranscriptSummary(report)}`,
110
+ "info"
111
+ );
112
+ showCommandPanel(context, output, {
113
+ title: "/remote doctor",
114
+ tone: "neutral",
115
+ summary: [
116
+ context.language === "en-US" ? "Remote doctor \u2014 Ctrl+O for details." : "\u8FDC\u7A0B\u8BCA\u65AD \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002"
117
+ ],
118
+ detailsText: report
119
+ });
120
+ return;
121
+ }
122
+ if (action === "setup") {
123
+ showCommandPanel(context, output, {
124
+ title: "/remote setup",
125
+ tone: "neutral",
126
+ summary: [
127
+ context.language === "en-US" ? "Remote setup now recommends /remote bot setup <channel> \u2014 Ctrl+O for details." : "\u8FDC\u7A0B\u63A5\u5165\u73B0\u5728\u63A8\u8350 /remote bot setup <channel> \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002"
128
+ ],
129
+ detailsText: [
130
+ formatRemoteBotSetupDetails(context, args[1]),
131
+ "",
132
+ "Legacy /remote setup details\uFF08compatibility\uFF09",
133
+ formatRemoteSetup(args[1], context)
134
+ ].join("\n")
135
+ });
136
+ return;
137
+ }
138
+ if (action === "test") {
139
+ const channel = findRemoteChannel(context, args[1]);
140
+ if (!channel) {
141
+ writeLine(output, "Remote test\uFF1A\u672A\u8BC6\u522B\u901A\u9053\u3002\u7528\u6CD5\uFF1A/remote test feishu|wecom|dingtalk");
142
+ return;
143
+ }
144
+ const event = createRemoteEvent(
145
+ channel,
146
+ "job_status",
147
+ "Remote channel test: Linghun redacted summary only.",
148
+ [],
149
+ 5 * 60 * 1e3
150
+ );
151
+ const result = await sendRemoteEventReal(context, event);
152
+ const ok = result.status === "sent";
153
+ await appendRemoteSystemEvent(
154
+ context,
155
+ `remote_test channel=${channel.id} status=${result.status} detail=${result.deliveryDetail ?? "none"} summary=${event.redactedSummary}`,
156
+ ok ? "info" : "warning"
157
+ );
158
+ showCommandPanel(context, output, {
159
+ title: "/remote test",
160
+ tone: ok ? "neutral" : result.status === "mock" ? "neutral" : "warning",
161
+ summary: [
162
+ context.language === "en-US" ? `Remote test ${channel.id} \xB7 ${result.status} \u2014 Ctrl+O for details.` : `\u8FDC\u7A0B\u6D4B\u8BD5 ${channel.id} \xB7 ${result.status} \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
163
+ ],
164
+ detailsText: formatRemoteTestResult(channel, result)
165
+ });
166
+ return;
167
+ }
168
+ if (action === "disable") {
169
+ const channel = findRemoteChannel(context, args[1]);
170
+ if (!channel) {
171
+ writeLine(output, "Remote disable\uFF1A\u672A\u8BC6\u522B\u901A\u9053\u3002\u7528\u6CD5\uFF1A/remote disable feishu|wecom|dingtalk");
172
+ return;
173
+ }
174
+ if (!context.remote.sessionDisabledChannelIds.includes(channel.id)) {
175
+ context.remote.sessionDisabledChannelIds.push(channel.id);
176
+ }
177
+ channel.runtimeStatus = "disabled";
178
+ channel.lastError = "disabled_by_user";
179
+ channel.nextAction = `/remote setup ${channel.id}`;
180
+ await appendRemoteSystemEvent(context, `remote_disabled channel=${channel.id}`, "info");
181
+ showCommandPanel(context, output, {
182
+ title: "/remote disable",
183
+ tone: "neutral",
184
+ summary: [
185
+ context.language === "en-US" ? `Remote channel disabled: ${channel.id} \u2014 Ctrl+O for details.` : `\u5DF2\u7981\u7528\u8FDC\u7A0B\u901A\u9053\uFF1A${channel.id} \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
186
+ ],
187
+ detailsText: `Remote channel disabled\uFF1A${channel.id}
188
+ - \u672C\u5730 TUI \u4E0D\u53D7\u5F71\u54CD\u3002
189
+ - \u5982\u9700\u91CD\u65B0\u8FDE\u63A5\uFF1A/remote setup ${channel.id}`
190
+ });
191
+ return;
192
+ }
193
+ if (action === "events") {
194
+ showCommandPanel(context, output, {
195
+ title: "/remote events",
196
+ tone: "neutral",
197
+ summary: [
198
+ context.language === "en-US" ? `Recent remote events: ${context.remote.events.length} \u2014 Ctrl+O for details.` : `\u6700\u8FD1\u8FDC\u7A0B\u4E8B\u4EF6\uFF1A${context.remote.events.length} \u6761 \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
199
+ ],
200
+ detailsText: formatRemoteEvents(context)
201
+ });
202
+ return;
203
+ }
204
+ if (action === "inbox") {
205
+ await handleRemoteInboxCommand(args.slice(1), context, output);
206
+ return;
207
+ }
208
+ if (action === "bridge") {
209
+ await handleRemoteBridgeCommand(args.slice(1), context, output);
210
+ return;
211
+ }
212
+ if (action === "bot") {
213
+ await handleRemoteBotCommand(args.slice(1), context, output);
214
+ return;
215
+ }
216
+ writeLine(
217
+ output,
218
+ "\u7528\u6CD5\uFF1A/remote bot doctor|setup|start|stop|pair|inbox <channel> | /remote setup <channel> | /remote test <channel> | /remote status | /remote doctor | /remote events | /remote inbox | /remote bridge doctor|pair|start|test-inbound|test-approval|test-status <channel> | /remote disable <channel>"
219
+ );
220
+ }
221
+ async function handleRemoteBotCommand(args, context, output) {
222
+ const action = args[0] ?? "doctor";
223
+ if (action === "inbox") {
224
+ await handleRemoteInboxCommand(args.slice(1), context, output);
225
+ return;
226
+ }
227
+ if (action === "doctor") {
228
+ const channelArg = args[1];
229
+ showCommandPanel(context, output, {
230
+ title: "/remote bot doctor",
231
+ tone: "neutral",
232
+ summary: [formatRemoteBotDoctorSummary(context, channelArg)],
233
+ detailsText: formatRemoteBotDoctorDetails(context, channelArg)
234
+ });
235
+ return;
236
+ }
237
+ if (action === "setup") {
238
+ const channelArg = args[1];
239
+ showCommandPanel(context, output, {
240
+ title: "/remote bot setup",
241
+ tone: "neutral",
242
+ summary: [formatRemoteBotSetupSummary(context, channelArg)],
243
+ detailsText: formatRemoteBotSetupDetails(context, channelArg)
244
+ });
245
+ return;
246
+ }
247
+ if (action === "start") {
248
+ const channelArg = normalizeRemoteChannelId(args[1] ?? "");
249
+ if (channelArg === "feishu") {
250
+ const channel = findRemoteChannel(context, channelArg);
251
+ if (!channel) {
252
+ writeLine(output, "Remote bot start\uFF1A\u672A\u8BC6\u522B Feishu Bot \u914D\u7F6E\u3002");
253
+ return;
254
+ }
255
+ const result = await startRemoteFeishuBridge(context, output, channel, "bot");
256
+ showCommandPanel(context, output, {
257
+ title: "/remote bot start feishu",
258
+ tone: result.status === "started" || result.status === "already_running" ? "neutral" : "warning",
259
+ summary: [toRemoteBotStartSummary("feishu", result.status)],
260
+ detailsText: [
261
+ toRemoteBotStartSummary("feishu", result.status),
262
+ result.detail,
263
+ "Bot path: Feishu messages enter RemoteInboundMessage -> handleRemoteInboundMessage; no second executor."
264
+ ].join("\n")
265
+ });
266
+ return;
267
+ }
268
+ showCommandPanel(context, output, {
269
+ title: `/remote bot start ${channelArg || "<channel>"}`,
270
+ tone: "warning",
271
+ summary: [formatRemoteBotStartBlockedSummary(channelArg)],
272
+ detailsText: [
273
+ formatRemoteBotStartBlockedSummary(channelArg),
274
+ formatRemoteBotSetupDetails(context, channelArg)
275
+ ].join("\n")
276
+ });
277
+ return;
278
+ }
279
+ if (action === "stop") {
280
+ const channelArg = normalizeRemoteChannelId(args[1] ?? "");
281
+ const stopped = await stopRemoteBotChannel(channelArg);
282
+ showCommandPanel(context, output, {
283
+ title: `/remote bot stop ${channelArg || "<channel>"}`,
284
+ tone: "neutral",
285
+ summary: [
286
+ stopped ? `Remote Bot ${channelArg} stopped.` : `Remote Bot ${channelArg || "channel"} is not running.`
287
+ ],
288
+ detailsText: stopped ? `Remote Bot ${channelArg} stopped.
289
+ Long connection handle was closed in this process. Secrets were not printed.` : "No active long connection handle exists in this process."
290
+ });
291
+ return;
292
+ }
293
+ if (action === "pair") {
294
+ const channelArg = normalizeRemoteChannelId(args[1] ?? "");
295
+ if (channelArg === "wechat") {
296
+ showCommandPanel(context, output, {
297
+ title: "/remote bot pair wechat",
298
+ tone: "warning",
299
+ summary: [
300
+ "Personal WeChat Bot pairing is experimental and blocked until an opt-in plugin bridge exists."
301
+ ],
302
+ detailsText: formatWechatBotExperimentalDetails()
303
+ });
304
+ return;
305
+ }
306
+ const channel = findRemoteChannel(context, channelArg);
307
+ if (!channel) {
308
+ writeLine(
309
+ output,
310
+ "Remote bot pair\uFF1A\u672A\u8BC6\u522B\u901A\u9053\u3002\u7528\u6CD5\uFF1A/remote bot pair feishu|dingtalk|wechat"
311
+ );
312
+ return;
313
+ }
314
+ const pairing = createRemotePairing(
315
+ context.remote,
316
+ channel,
317
+ context.projectPath,
318
+ await deps().ensureSession(context)
319
+ );
320
+ await appendRemoteSystemEvent(
321
+ context,
322
+ `remote_bot_pair channel=${channel.id} status=${pairing.status} expiresAt=${pairing.status === "created" ? pairing.pairing.expiresAt : "none"}`,
323
+ "info"
324
+ );
325
+ showCommandPanel(context, output, {
326
+ title: `/remote bot pair ${channel.id}`,
327
+ tone: pairing.status === "created" ? "neutral" : "warning",
328
+ summary: [
329
+ pairing.status === "created" ? `Bot pairing code ${pairing.pairing.code} \u2014 send /bind CODE from your Bot chat.` : pairing.summary
330
+ ],
331
+ detailsText: [
332
+ pairing.status === "created" ? `Bot pairing code ${pairing.pairing.code} \u2014 send /bind CODE from your Bot chat.` : pairing.summary,
333
+ formatRemotePairing(pairing)
334
+ ].join("\n")
335
+ });
336
+ return;
337
+ }
338
+ writeLine(
339
+ output,
340
+ "\u7528\u6CD5\uFF1A/remote bot doctor [channel] | /remote bot setup feishu|dingtalk|wechat | /remote bot start feishu|dingtalk|wechat | /remote bot stop <channel> | /remote bot pair <channel> | /remote bot inbox"
341
+ );
342
+ }
343
+ async function handleRemoteInboxCommand(args, context, output) {
344
+ const action = args[0] ?? "list";
345
+ let detail = formatRemoteInbox(context.remote);
346
+ if (action === "clear") {
347
+ const count = clearRemoteInbox(context.remote);
348
+ detail = `Remote inbox cleared\uFF1A${count}`;
349
+ } else if (action === "reject") {
350
+ const id = args[1] ?? "";
351
+ detail = rejectRemoteInboxItem(context.remote, id) ? `Remote inbox rejected\uFF1A${id}` : `Remote inbox item not found\uFF1A${id}`;
352
+ } else if (action === "drain") {
353
+ const drained = drainRemoteInbox(context.remote);
354
+ detail = `Remote inbox drain/export and clear\uFF1A${drained.length}
355
+ ${drained.map((item) => `- ${item.id}: ${item.channel}; ${item.text}`).join(
356
+ "\n"
357
+ )}
358
+ These messages were exported and cleared only; they were not sent to sendMessage.`;
359
+ }
360
+ await appendRemoteSystemEvent(
361
+ context,
362
+ `remote_inbox action=${action} size=${context.remote.inbox.length}`,
363
+ "info"
364
+ );
365
+ showCommandPanel(context, output, {
366
+ title: "/remote inbox",
367
+ tone: "neutral",
368
+ summary: [
369
+ context.language === "en-US" ? `Remote inbox: ${context.remote.inbox.length} queued \u2014 Ctrl+O for details.` : `\u8FDC\u7A0B\u6536\u4EF6\u7BB1\uFF1A${context.remote.inbox.length} \u6761\u6392\u961F \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
370
+ ],
371
+ detailsText: detail
372
+ });
373
+ }
374
+ async function handleRemoteBridgeCommand(args, context, output) {
375
+ const action = args[0] ?? "doctor";
376
+ if (action === "pair" && args[1] === "status") {
377
+ showCommandPanel(context, output, {
378
+ title: "/remote bridge pair status",
379
+ tone: "neutral",
380
+ summary: ["Remote pairing status \u2014 Ctrl+O for details."],
381
+ detailsText: formatRemotePairingStatus(context.remote)
382
+ });
383
+ return;
384
+ }
385
+ if (action === "pair" && args[1] === "cancel") {
386
+ const decision = cancelRemotePairing(context.remote, args[2]);
387
+ await appendRemoteSystemEvent(context, `remote_pair_cancel status=${decision.status}`, "info");
388
+ showCommandPanel(context, output, {
389
+ title: "/remote bridge pair cancel",
390
+ tone: decision.status === "cancelled" ? "neutral" : "warning",
391
+ summary: [decision.summary],
392
+ detailsText: decision.summary
393
+ });
394
+ return;
395
+ }
396
+ if (action === "start") {
397
+ const channel2 = findRemoteChannel(context, args[1]);
398
+ if (!channel2) {
399
+ writeLine(output, "Remote bridge start\uFF1A\u672A\u8BC6\u522B\u901A\u9053\u3002\u7528\u6CD5\uFF1A/remote bridge start feishu");
400
+ return;
401
+ }
402
+ if (channel2.id !== "feishu" && channel2.config.type !== "lark") {
403
+ writeLine(output, "Remote bridge start\uFF1A\u5F53\u524D\u53EA\u652F\u6301 feishu/lark \u957F\u8FDE\u63A5\u3002");
404
+ return;
405
+ }
406
+ const result = await startRemoteFeishuBridge(context, output, channel2);
407
+ showCommandPanel(context, output, {
408
+ title: "/remote bridge start",
409
+ tone: result.status === "started" || result.status === "already_running" ? "neutral" : "warning",
410
+ summary: [result.summary],
411
+ detailsText: result.detail
412
+ });
413
+ return;
414
+ }
415
+ if (action === "local-register") {
416
+ const clientId = args[1] ?? "local";
417
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
418
+ type: "register",
419
+ clientId
420
+ });
421
+ showCommandPanel(context, output, {
422
+ title: "/remote bridge local-register",
423
+ tone: "neutral",
424
+ summary: [`Local REPL bridge ${decision.status}: ${clientId}`],
425
+ detailsText: JSON.stringify(decision, null, 2)
426
+ });
427
+ return;
428
+ }
429
+ if (action === "local-listen") {
430
+ const socketPath = args[1] ?? createDefaultReplBridgeSocketPath(context.projectPath);
431
+ if (context.remote.localReplBridgeSocket) {
432
+ showCommandPanel(context, output, {
433
+ title: "/remote bridge local-listen",
434
+ tone: "neutral",
435
+ summary: [`Local REPL bridge socket already listening: ${context.remote.localReplBridgeSocket.socketPath}`],
436
+ detailsText: "Existing local REPL bridge socket is active in this process."
437
+ });
438
+ return;
439
+ }
440
+ const handle = await startReplBridgeSocketServer({
441
+ socketPath,
442
+ bridge: () => ensureLocalReplBridge(context),
443
+ remote: () => context.remote
444
+ });
445
+ context.remote.localReplBridgeSocket = handle;
446
+ showCommandPanel(context, output, {
447
+ title: "/remote bridge local-listen",
448
+ tone: "neutral",
449
+ summary: [`Local REPL bridge socket listening: ${handle.socketPath}`],
450
+ detailsText: [
451
+ `socketPath: ${handle.socketPath}`,
452
+ "Protocol: JSONL ReplBridgeMessage in, ReplBridgeDecision out.",
453
+ "Messages still require /remote bridge local-route or an existing inbound handler path; no second executor was created."
454
+ ].join("\n")
455
+ });
456
+ return;
457
+ }
458
+ if (action === "local-close") {
459
+ const handle = context.remote.localReplBridgeSocket;
460
+ if (!handle) {
461
+ showCommandPanel(context, output, {
462
+ title: "/remote bridge local-close",
463
+ tone: "neutral",
464
+ summary: ["Local REPL bridge socket is not listening."],
465
+ detailsText: "No active local REPL bridge socket exists in this process."
466
+ });
467
+ return;
468
+ }
469
+ await handle.close();
470
+ context.remote.localReplBridgeSocket = void 0;
471
+ showCommandPanel(context, output, {
472
+ title: "/remote bridge local-close",
473
+ tone: "neutral",
474
+ summary: [`Local REPL bridge socket closed: ${handle.socketPath}`],
475
+ detailsText: "Local REPL bridge socket closed; queued bridge state remains in this session."
476
+ });
477
+ return;
478
+ }
479
+ if (action === "local-inbound") {
480
+ const clientId = args[1] ?? "local";
481
+ const text = args.slice(2).join(" ").trim();
482
+ if (!text) {
483
+ writeLine(output, "\u7528\u6CD5\uFF1A/remote bridge local-inbound <clientId> <text>");
484
+ return;
485
+ }
486
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
487
+ type: "inbound",
488
+ clientId,
489
+ text
490
+ });
491
+ showCommandPanel(context, output, {
492
+ title: "/remote bridge local-inbound",
493
+ tone: decision.status === "accepted" ? "neutral" : "warning",
494
+ summary: [`Local REPL bridge ${decision.status}: ${clientId}`],
495
+ detailsText: JSON.stringify(decision, null, 2)
496
+ });
497
+ return;
498
+ }
499
+ if (action === "local-poll") {
500
+ const clientId = args[1] ?? "local";
501
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
502
+ type: "poll",
503
+ clientId
504
+ });
505
+ showCommandPanel(context, output, {
506
+ title: "/remote bridge local-poll",
507
+ tone: decision.status === "polled" ? "neutral" : "warning",
508
+ summary: [
509
+ decision.status === "polled" ? `Local REPL bridge polled: ${(decision.messages ?? []).length} message(s)` : `Local REPL bridge ${decision.status}`
510
+ ],
511
+ detailsText: JSON.stringify(decision, null, 2)
512
+ });
513
+ return;
514
+ }
515
+ if (action === "local-route") {
516
+ const clientId = args[1] ?? "local";
517
+ const inbound = deps().handleRemoteInboundMessage;
518
+ if (!inbound) {
519
+ showCommandPanel(context, output, {
520
+ title: "/remote bridge local-route",
521
+ tone: "warning",
522
+ summary: ["Local REPL bridge route blocked: inbound handler unavailable."],
523
+ detailsText: "Local bridge messages must route through handleRemoteInboundMessage; no fallback executor was used."
524
+ });
525
+ return;
526
+ }
527
+ const poll = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
528
+ type: "poll",
529
+ clientId
530
+ });
531
+ if (poll.status !== "polled") {
532
+ showCommandPanel(context, output, {
533
+ title: "/remote bridge local-route",
534
+ tone: "warning",
535
+ summary: [`Local REPL bridge ${poll.status}: ${clientId}`],
536
+ detailsText: JSON.stringify(poll, null, 2)
537
+ });
538
+ return;
539
+ }
540
+ let routed = 0;
541
+ const decisions = [];
542
+ for (const message of poll.messages) {
543
+ const decision = await inbound(message, context, void 0, output);
544
+ decisions.push(`${message.messageId}: ${decision.status}`);
545
+ if (decision.status === "accepted" || decision.status === "approved") {
546
+ routed += 1;
547
+ handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
548
+ type: "acknowledge",
549
+ clientId,
550
+ messageId: message.messageId
551
+ });
552
+ }
553
+ }
554
+ showCommandPanel(context, output, {
555
+ title: "/remote bridge local-route",
556
+ tone: "neutral",
557
+ summary: [`Local REPL bridge routed: ${routed}/${poll.messages.length} message(s)`],
558
+ detailsText: [
559
+ "Messages were routed through handleRemoteInboundMessage; no second executor was used.",
560
+ ...decisions
561
+ ].join("\n")
562
+ });
563
+ return;
564
+ }
565
+ if (action === "local-ack") {
566
+ const clientId = args[1] ?? "local";
567
+ const messageId = args[2] ?? "";
568
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
569
+ type: "acknowledge",
570
+ clientId,
571
+ messageId
572
+ });
573
+ showCommandPanel(context, output, {
574
+ title: "/remote bridge local-ack",
575
+ tone: decision.status === "acknowledged" ? "neutral" : "warning",
576
+ summary: [`Local REPL bridge ${decision.status}: ${messageId}`],
577
+ detailsText: JSON.stringify(decision, null, 2)
578
+ });
579
+ return;
580
+ }
581
+ if (action === "local-heartbeat") {
582
+ const clientId = args[1] ?? "local";
583
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
584
+ type: "heartbeat",
585
+ clientId,
586
+ now: (/* @__PURE__ */ new Date()).toISOString()
587
+ });
588
+ showCommandPanel(context, output, {
589
+ title: "/remote bridge local-heartbeat",
590
+ tone: decision.status === "heartbeat" ? "neutral" : "warning",
591
+ summary: [`Local REPL bridge ${decision.status}: ${clientId}`],
592
+ detailsText: JSON.stringify(decision, null, 2)
593
+ });
594
+ return;
595
+ }
596
+ if (action === "local-stop") {
597
+ const clientId = args[1] ?? "local";
598
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
599
+ type: "stop",
600
+ clientId,
601
+ reason: "user requested stop"
602
+ });
603
+ showCommandPanel(context, output, {
604
+ title: "/remote bridge local-stop",
605
+ tone: "neutral",
606
+ summary: [`Local REPL bridge ${decision.status}: ${clientId}`],
607
+ detailsText: JSON.stringify(decision, null, 2)
608
+ });
609
+ return;
610
+ }
611
+ if (action === "local-deregister") {
612
+ const clientId = args[1] ?? "local";
613
+ const decision = handleReplBridgeMessage(ensureLocalReplBridge(context), context.remote, {
614
+ type: "deregister",
615
+ clientId
616
+ });
617
+ showCommandPanel(context, output, {
618
+ title: "/remote bridge local-deregister",
619
+ tone: decision.status === "deregistered" ? "neutral" : "warning",
620
+ summary: [`Local REPL bridge ${decision.status}: ${clientId}`],
621
+ detailsText: JSON.stringify(decision, null, 2)
622
+ });
623
+ return;
624
+ }
625
+ if (action === "jwt-refresh-check") {
626
+ const result = await maybeRefreshJwtToken({
627
+ token: args[1],
628
+ expiresAt: args[2],
629
+ refresh: async () => ({
630
+ token: "refreshed-token-redacted",
631
+ expiresAt: new Date(Date.now() + 60 * 60 * 1e3).toISOString()
632
+ })
633
+ });
634
+ showCommandPanel(context, output, {
635
+ title: "/remote bridge jwt-refresh-check",
636
+ tone: "neutral",
637
+ summary: [`JWT refresh: ${result.refreshed ? "refreshed" : result.reason}`],
638
+ detailsText: JSON.stringify(
639
+ result.refreshed ? { ...result, token: "[REDACTED]" } : result,
640
+ null,
641
+ 2
642
+ )
643
+ });
644
+ return;
645
+ }
646
+ const channel = findRemoteChannel(context, args[1]);
647
+ if (!channel) {
648
+ writeLine(
649
+ output,
650
+ "Remote bridge\uFF1A\u672A\u8BC6\u522B\u901A\u9053\u3002\u7528\u6CD5\uFF1A/remote bridge doctor|pair|start|test-inbound|test-approval|test-status feishu|dingtalk|wecom"
651
+ );
652
+ return;
653
+ }
654
+ const report = getRemoteBridgeDoctor(context.remote, channel.id);
655
+ if (action === "pair") {
656
+ if (report.readiness === "notification-only") {
657
+ showCommandPanel(context, output, {
658
+ title: "/remote bridge pair",
659
+ tone: "warning",
660
+ summary: [
661
+ context.language === "en-US" ? "Pairing blocked: webhook is notification-only." : "\u7ED1\u5B9A\u88AB\u963B\u65AD\uFF1Awebhook \u53EA\u80FD\u901A\u77E5\uFF0C\u4E0D\u80FD\u771F\u5B9E\u7ED1\u5B9A\u3002"
662
+ ],
663
+ detailsText: [
664
+ formatRemoteBridgeDoctor(report),
665
+ "Pairing needs a platform app plus callback/daemon; do not treat webhook as mobile control."
666
+ ].join("\n")
667
+ });
668
+ return;
669
+ }
670
+ const pairing = createRemotePairing(
671
+ context.remote,
672
+ channel,
673
+ context.projectPath,
674
+ await deps().ensureSession(context)
675
+ );
676
+ await appendRemoteSystemEvent(
677
+ context,
678
+ `remote_pair_create channel=${channel.id} status=${pairing.status} expiresAt=${pairing.status === "created" ? pairing.pairing.expiresAt : "none"}`,
679
+ "info"
680
+ );
681
+ showCommandPanel(context, output, {
682
+ title: "/remote bridge pair",
683
+ tone: "neutral",
684
+ summary: [
685
+ pairing.status === "created" ? `Pairing code ${pairing.pairing.code} \u2014 Ctrl+O for details.` : pairing.summary
686
+ ],
687
+ detailsText: formatRemotePairing(pairing)
688
+ });
689
+ return;
690
+ }
691
+ if (action === "doctor") {
692
+ showCommandPanel(context, output, {
693
+ title: "/remote bridge doctor",
694
+ tone: report.readiness === "notification-only" ? "warning" : "neutral",
695
+ summary: [
696
+ context.language === "en-US" ? `Bridge ${channel.id}: ${report.readiness} \u2014 Ctrl+O for details.` : `\u624B\u673A\u6865\u63A5 ${channel.id}: ${report.readiness} \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
697
+ ],
698
+ detailsText: formatRemoteBridgeDoctor(report)
699
+ });
700
+ return;
701
+ }
702
+ if (action === "test-inbound" || action === "test-status" || action === "test-approval") {
703
+ if (!report.canRunLocalFixture) {
704
+ showCommandPanel(context, output, {
705
+ title: `/remote bridge ${action}`,
706
+ tone: "warning",
707
+ summary: [
708
+ context.language === "en-US" ? `Bridge fixture blocked: ${report.readiness} \u2014 Ctrl+O for details.` : `\u6865\u63A5 fixture \u88AB\u963B\u65AD\uFF1A${report.readiness} \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
709
+ ],
710
+ detailsText: formatRemoteBridgeDoctor(report)
711
+ });
712
+ return;
713
+ }
714
+ const kind = action === "test-approval" ? "approval_response" : action === "test-status" ? "status_query" : "natural_language_message";
715
+ const event = kind === "approval_response" ? context.remote.events.find(
716
+ (item) => item.channel === channel.id && item.eventType === "approval_request"
717
+ ) : void 0;
718
+ const fixture = createSignedRemoteInboundFixture(channel, {
719
+ kind,
720
+ text: kind === "natural_language_message" ? "D.14F deterministic bridge fixture" : void 0,
721
+ eventId: event?.id,
722
+ nonce: event?.nonce,
723
+ approve: kind === "approval_response" ? true : void 0
724
+ });
725
+ const decision = processRemoteInbound(context, fixture);
726
+ await appendRemoteSystemEvent(
727
+ context,
728
+ `remote_bridge_fixture channel=${channel.id} kind=${kind} status=${decision.status} summary=${decision.summary}`,
729
+ decision.status === "accepted" || decision.status === "approved" ? "info" : "warning"
730
+ );
731
+ showCommandPanel(context, output, {
732
+ title: `/remote bridge ${action}`,
733
+ tone: decision.status === "accepted" || decision.status === "approved" ? "neutral" : "warning",
734
+ summary: [
735
+ context.language === "en-US" ? `Bridge fixture ${channel.id}: ${decision.status} \u2014 Ctrl+O for details.` : `\u6865\u63A5 fixture ${channel.id}: ${decision.status} \u2014 Ctrl+O \u67E5\u770B\u8BE6\u60C5\u3002`
736
+ ],
737
+ detailsText: [
738
+ formatRemoteBridgeDoctor(report),
739
+ `fixture kind: ${kind}`,
740
+ `decision: ${decision.status}`,
741
+ `summary: ${decision.summary}`
742
+ ].join("\n")
743
+ });
744
+ return;
745
+ }
746
+ writeLine(
747
+ output,
748
+ "\u7528\u6CD5\uFF1A/remote bridge doctor|pair|start|test-inbound|test-approval|test-status feishu|dingtalk|wecom | local-listen|local-close|local-register|local-inbound|local-poll|local-route|local-ack|local-heartbeat|local-stop|local-deregister"
749
+ );
750
+ }
751
+ function ensureLocalReplBridge(context) {
752
+ if (!context.remote.localReplBridge) {
753
+ context.remote.localReplBridge = createReplBridgeState();
754
+ }
755
+ return context.remote.localReplBridge;
756
+ }
757
+ async function startRemoteFeishuBridge(context, output, channel, readinessMode = "bridge") {
758
+ const report = readinessMode === "bridge" ? getRemoteBridgeDoctor(context.remote, channel.id) : void 0;
759
+ const botReadiness = readinessMode === "bot" ? getFeishuBotStartReadiness(channel) : void 0;
760
+ const blockedDetail = botReadiness ? formatFeishuBotStartReadiness(botReadiness) : report ? formatRemoteBridgeDoctor(report) : "";
761
+ if (botReadiness?.readiness === "notification-only" || report?.readiness === "notification-only") {
762
+ return {
763
+ status: "blocked",
764
+ summary: "Feishu bridge start blocked: webhook is notification-only.",
765
+ detail: blockedDetail
766
+ };
767
+ }
768
+ if (botReadiness && !botReadiness.canStart) {
769
+ return {
770
+ status: "blocked",
771
+ summary: `Feishu bridge start blocked: ${botReadiness.readiness}.`,
772
+ detail: blockedDetail
773
+ };
774
+ }
775
+ if (report && report.readiness !== "ready-to-start" && report.readiness !== "fixture-ready") {
776
+ return {
777
+ status: "blocked",
778
+ summary: `Feishu bridge start blocked: ${report.readiness}.`,
779
+ detail: blockedDetail
780
+ };
781
+ }
782
+ const appId = resolveEnvRef(channel.config.appIdRef);
783
+ const appSecret = resolveEnvRef(channel.config.appSecretRef);
784
+ if (!appId || !appSecret) {
785
+ const missing = [
786
+ appId ? void 0 : "LINGHUN_REMOTE_FEISHU_APP_ID",
787
+ appSecret ? void 0 : "LINGHUN_REMOTE_FEISHU_APP_SECRET"
788
+ ].filter((item) => Boolean(item));
789
+ return {
790
+ status: "blocked",
791
+ summary: "Feishu bridge start blocked: app env missing.",
792
+ detail: [
793
+ blockedDetail,
794
+ `missing env: ${missing.join(", ")}`,
795
+ "Secret values are not printed."
796
+ ].join("\n")
797
+ };
798
+ }
799
+ if (feishuLongConnectionHandles.has(channel.id)) {
800
+ return {
801
+ status: "already_running",
802
+ summary: "Feishu bridge already running.",
803
+ detail: "Long connection handle is active in this process; secrets are not printed."
804
+ };
805
+ }
806
+ try {
807
+ const start = deps().startFeishuLongConnection ?? startFeishuLongConnection;
808
+ const handle = await start({
809
+ appId,
810
+ appSecret,
811
+ onMessage: async (message) => {
812
+ const inbound = deps().handleRemoteInboundMessage;
813
+ if (!inbound) return;
814
+ await inbound(message, context, void 0, output);
815
+ }
816
+ });
817
+ feishuLongConnectionHandles.set(channel.id, handle);
818
+ await appendRemoteSystemEvent(
819
+ context,
820
+ `remote_bridge_start channel=${channel.id} status=started`,
821
+ "info"
822
+ );
823
+ return {
824
+ status: "started",
825
+ summary: "Feishu bridge started: waiting for mobile messages.",
826
+ detail: [
827
+ "Long connection started with official SDK; secrets are not printed.",
828
+ botReadiness ? formatFeishuBotStartReadiness(botReadiness) : void 0
829
+ ].filter((item) => Boolean(item)).join("\n")
830
+ };
831
+ } catch {
832
+ await appendRemoteSystemEvent(
833
+ context,
834
+ `remote bridge start: channel ${channel.id}; status failed`,
835
+ "warning"
836
+ );
837
+ return {
838
+ status: "failed",
839
+ summary: "Feishu bridge start failed: platform connection rejected or unavailable.",
840
+ detail: [
841
+ blockedDetail,
842
+ "Check Feishu app credentials, long connection event subscription, bot ability, and message permissions."
843
+ ].join("\n")
844
+ };
845
+ }
846
+ }
847
+ function getFeishuBotStartReadiness(channel) {
848
+ if (channel.config.transport === "webhook" || channel.config.transport === "webhook_mock") {
849
+ return { readiness: "notification-only", canStart: false, missingEnv: [] };
850
+ }
851
+ if (channel.config.transport !== "official_cli") {
852
+ return { readiness: "needs-event-subscription", canStart: false, missingEnv: [] };
853
+ }
854
+ if (channel.config.inboundMode !== "callback" || channel.config.callbackEndpoint !== "feishu-long-connection") {
855
+ return { readiness: "needs-event-subscription", canStart: false, missingEnv: [] };
856
+ }
857
+ if (!channel.config.appIdRef) {
858
+ return { readiness: "needs-app-id", canStart: false, missingEnv: [] };
859
+ }
860
+ if (!channel.config.appSecretRef) {
861
+ return { readiness: "needs-app-secret", canStart: false, missingEnv: [] };
862
+ }
863
+ const missingEnv = [channel.config.appIdRef, channel.config.appSecretRef].filter(
864
+ (ref) => !resolveEnvRef(ref)
865
+ );
866
+ if (missingEnv.length) {
867
+ return { readiness: "needs-env", canStart: false, missingEnv };
868
+ }
869
+ if (channel.config.bindingUserId && channel.config.trustedSources.length > 0) {
870
+ return { readiness: "bound-ready", canStart: true, missingEnv: [] };
871
+ }
872
+ return { readiness: "ready-to-start", canStart: true, missingEnv: [] };
873
+ }
874
+ function formatFeishuBotStartReadiness(readiness) {
875
+ const lines = [
876
+ "Feishu Bot start readiness",
877
+ `- readiness: ${readiness.readiness}`,
878
+ `- can start: ${readiness.canStart ? "yes" : "no"}`
879
+ ];
880
+ if (readiness.missingEnv.length) {
881
+ lines.push(`- missing env: ${readiness.missingEnv.join(", ")}`);
882
+ }
883
+ lines.push("- secret values are not printed.");
884
+ lines.push("- ready-to-start means Bot can run and wait for /bind CODE.");
885
+ lines.push(
886
+ "- bound/ready means ordinary mobile messages can pass binding/trusted-source checks."
887
+ );
888
+ lines.push("- webhook path remains notification-only.");
889
+ return lines.join("\n");
890
+ }
891
+ async function stopRemoteBotChannel(channelId) {
892
+ const handle = feishuLongConnectionHandles.get(channelId);
893
+ if (!handle) return false;
894
+ await handle.close();
895
+ feishuLongConnectionHandles.delete(channelId);
896
+ return true;
897
+ }
898
+ function resolveEnvRef(ref) {
899
+ if (!ref) return void 0;
900
+ return process.env[ref];
901
+ }
902
+ function formatRemoteEvents(context) {
903
+ if (context.remote.events.length === 0) {
904
+ return "Remote events\uFF1A\u6682\u65E0\u8FDC\u7A0B\u4E8B\u4EF6\u3002\u8FD0\u884C /remote test <channel> \u53D1\u9001\u4E00\u6761\u8131\u654F\u6D4B\u8BD5\u6458\u8981\u3002";
905
+ }
906
+ const lines = ["Remote events\uFF08\u6700\u8FD1\u5728\u524D\uFF0C\u4EC5\u8131\u654F\u6458\u8981\uFF0C\u4E0D\u542B secret/endpoint/\u6B63\u6587\uFF09"];
907
+ for (const event of context.remote.events.slice(0, 10)) {
908
+ lines.push(
909
+ `- ${event.channel} \xB7 ${event.eventType} \xB7 ${event.status}${event.deliveryDetail ? ` \xB7 ${event.deliveryDetail}` : ""}`
910
+ );
911
+ lines.push(` summary: ${event.redactedSummary}`);
912
+ }
913
+ return lines.join("\n");
914
+ }
915
+ function formatRemoteDoctor(context) {
916
+ const lines = [
917
+ `Remote Doctor\uFF1A${context.remote.enabled ? "enabled" : "disabled"}\uFF1B\u5931\u8D25\u4F1A\u964D\u7EA7\u4E3A disabled/blocked\uFF0C\u4E0D\u963B\u585E\u4E3B TUI\u3002`,
918
+ "\u63A8\u8350\u5165\u53E3\uFF1A/remote bot doctor\uFF1B\u65E7 /remote bridge \u547D\u4EE4\u4FDD\u7559\u517C\u5BB9\u3002"
919
+ ];
920
+ for (const channel of context.remote.channels) {
921
+ const grade = getRemoteCapabilityGrade(channel);
922
+ const bridge = getRemoteBridgeDoctor(context.remote, channel.id);
923
+ lines.push(`- ${channel.id}: ${channel.runtimeStatus}`);
924
+ lines.push(` binding: ${channel.bindingStatus}`);
925
+ lines.push(` transport: ${channel.config.transport}; status ${channel.transportStatus}`);
926
+ lines.push(` capability: ${grade.grade} \u2014 ${grade.reason}`);
927
+ lines.push(` bridge: ${bridge.readiness}; ${bridge.nextAction}`);
928
+ lines.push(` last error: ${channel.lastError ?? "none"}`);
929
+ lines.push(` allowed events: ${channel.config.allowedEventTypes.join(", ")}`);
930
+ lines.push(` next action: ${channel.nextAction}`);
931
+ }
932
+ lines.push(
933
+ "Secrets/endpoints are redacted. webhook/webhook_mock \u4EC5\u5355\u5411\u901A\u77E5\uFF1B\u5BA1\u6279/\u81EA\u7136\u8BED\u8A00\u56DE\u4F20\u9700\u5B98\u65B9 CLI/\u5E94\u7528\u5165\u7AD9\u80FD\u529B\u3002"
934
+ );
935
+ return lines.join("\n");
936
+ }
937
+ function formatRemoteBotDoctorSummary(context, channelArg) {
938
+ const channels = selectBotChannels(context, channelArg);
939
+ if (channels.length === 0 && normalizeRemoteChannelId(channelArg ?? "") === "wechat") {
940
+ return "Personal WeChat Bot: experimental blocked.";
941
+ }
942
+ if (channels.length === 0) return "Remote Bot doctor: choose feishu, dingtalk, or wechat.";
943
+ return channels.map((channel) => `${formatBotChannelName(channel.id)}: ${getRemoteBotUserStatus(channel)}`).join("\uFF1B");
944
+ }
945
+ function formatRemoteBotDoctorDetails(context, channelArg) {
946
+ const normalized = normalizeRemoteChannelId(channelArg ?? "");
947
+ if (normalized === "wechat") return formatWechatBotExperimentalDetails();
948
+ const channels = selectBotChannels(context, channelArg);
949
+ if (channels.length === 0) {
950
+ return "Remote Bot doctor\uFF1A\u8BF7\u9009\u62E9 feishu\u3001dingtalk \u6216 wechat\u3002";
951
+ }
952
+ const lines = [
953
+ "Remote Bot doctor\uFF08\u666E\u901A\u89C6\u56FE\u53EA\u663E\u793A Bot \u72B6\u6001\uFF1B\u5E95\u5C42 bridge \u8BCA\u65AD\u8BF7\u7528 /remote bridge doctor\uFF09"
954
+ ];
955
+ for (const channel of channels) {
956
+ const status = getRemoteBotUserStatus(channel);
957
+ lines.push(`- ${formatBotChannelName(channel.id)}: ${status}`);
958
+ lines.push(` setup: /remote bot setup ${channel.id}`);
959
+ lines.push(` start: /remote bot start ${channel.id}`);
960
+ lines.push(` pair: /remote bot pair ${channel.id}`);
961
+ if (channel.id === "feishu") {
962
+ lines.push(
963
+ " needs: App ID, App Secret, Bot enabled, long connection, message receive event."
964
+ );
965
+ lines.push(
966
+ ` readiness: ${formatFeishuBotStartReadiness(getFeishuBotStartReadiness(channel)).replace(/\n/g, "\n ")}`
967
+ );
968
+ } else if (channel.id === "dingtalk") {
969
+ lines.push(" needs: Client ID, Client Secret, robot Stream mode, published app.");
970
+ lines.push(" real Stream smoke: NOT RUN without credentials and a published DingTalk bot.");
971
+ }
972
+ lines.push(
973
+ ` pairing: ${channel.config.bindingUserId && channel.config.trustedSources.length > 0 ? "complete" : "needed"}`
974
+ );
975
+ }
976
+ lines.push(
977
+ "Secrets, endpoints, trusted source lists, binding ids, callback refs, QR/session tokens, and raw payloads are redacted."
978
+ );
979
+ lines.push(
980
+ "Webhook notification remains notification-only; Bot inbound must enter the existing RemoteInboundMessage main chain."
981
+ );
982
+ return lines.join("\n");
983
+ }
984
+ function formatRemoteBotSetupSummary(context, channelArg) {
985
+ const normalized = normalizeRemoteChannelId(channelArg ?? "");
986
+ if (normalized === "wechat") return "Personal WeChat Bot setup: experimental opt-in only.";
987
+ const channel = findRemoteChannel(context, normalized);
988
+ if (!channel) return "Remote Bot setup: choose feishu, dingtalk, or wechat.";
989
+ return `${formatBotChannelName(channel.id)} setup: ${getRemoteBotUserStatus(channel)}.`;
990
+ }
991
+ function formatRemoteBotSetupDetails(context, channelArg) {
992
+ const normalized = normalizeRemoteChannelId(channelArg ?? "");
993
+ if (normalized === "wechat") return formatWechatBotExperimentalDetails();
994
+ const channel = findRemoteChannel(context, normalized);
995
+ if (!channel) {
996
+ return "Remote Bot setup\uFF1A\u8BF7\u9009\u62E9 feishu\u3001dingtalk \u6216 wechat\u3002\u793A\u4F8B\uFF1A/remote bot setup feishu";
997
+ }
998
+ if (channel.id === "feishu") return formatFeishuBotSetupDetails(channel);
999
+ if (channel.id === "dingtalk") return formatDingTalkBotSetupDetails(channel);
1000
+ return "Enterprise WeChat is future/not implemented in this Bot productization stage.";
1001
+ }
1002
+ function formatFeishuBotSetupDetails(channel) {
1003
+ return [
1004
+ "Feishu Bot setup",
1005
+ "- Prepare a Feishu/Lark app with Bot enabled.",
1006
+ "- Enable long connection and subscribe to im.message.receive_v1.",
1007
+ "- Set env refs: LINGHUN_REMOTE_FEISHU_APP_ID and LINGHUN_REMOTE_FEISHU_APP_SECRET.",
1008
+ "- Webhook notification is optional and not required for mobile control.",
1009
+ "- Start: /remote bot start feishu.",
1010
+ "- Pair from the Bot chat: /remote bot pair feishu, then send /bind CODE.",
1011
+ `- Current Bot status: ${getRemoteBotUserStatus(channel)}.`,
1012
+ "- Secret values are never printed or stored in reports."
1013
+ ].join("\n");
1014
+ }
1015
+ function formatDingTalkBotSetupDetails(channel) {
1016
+ return [
1017
+ "DingTalk Bot setup",
1018
+ "- Official Stream path uses the dingtalk-stream SDK and Client ID / Client Secret.",
1019
+ "- In DingTalk developer console, enable robot message receiving, choose Stream mode, and publish the app.",
1020
+ "- Robot message topic: /v1.0/im/bot/messages/get.",
1021
+ "- Card callback topic: /v1.0/card/instances/callback; advanced cards must set callbackType=STREAM.",
1022
+ "- Configure env refs as LINGHUN_REMOTE_DINGTALK_CLIENT_ID and LINGHUN_REMOTE_DINGTALK_CLIENT_SECRET.",
1023
+ "- Current build has offline adapter normalization only; real DingTalk Stream start is NOT RUN without SDK wiring and credentials.",
1024
+ `- Current Bot status: ${getRemoteBotUserStatus(channel)}.`,
1025
+ "- sessionWebhook and Client Secret are treated as secrets and never persisted in summaries."
1026
+ ].join("\n");
1027
+ }
1028
+ function formatWechatBotExperimentalDetails() {
1029
+ return [
1030
+ "Personal WeChat Bot setup: experimental",
1031
+ "- Default: blocked. Set LINGHUN_REMOTE_WECHAT_EXPERIMENTAL=1 only after accepting QR-login, third-party puppet/provider, account restriction, token, and session risks.",
1032
+ "- No Wechaty/PadLocal dependency is bundled in Linghun core in this stage.",
1033
+ "- A real plugin bridge must provide proof before messages can become RemoteInboundMessage.",
1034
+ "- QR payloads, QR images, session files, wxid/openid/unionid, puppet tokens, cookies, and device data must be redacted.",
1035
+ "- No fake WeChat inbound PASS is allowed; without a real opted-in bridge, /remote bot start wechat remains blocked."
1036
+ ].join("\n");
1037
+ }
1038
+ function formatRemoteBotStartBlockedSummary(channelId) {
1039
+ if (channelId === "dingtalk")
1040
+ return "DingTalk Bot start blocked: Stream SDK wiring and real credentials are not configured.";
1041
+ if (channelId === "wechat") {
1042
+ return process.env.LINGHUN_REMOTE_WECHAT_EXPERIMENTAL === "1" ? "Personal WeChat Bot start blocked: experimental plugin bridge is not installed." : "Personal WeChat Bot start blocked: experimental opt-in is disabled.";
1043
+ }
1044
+ return "Remote Bot start blocked: choose feishu, dingtalk, or wechat.";
1045
+ }
1046
+ function toRemoteBotStartSummary(channelId, status) {
1047
+ if (status === "started")
1048
+ return `${formatBotChannelName(channelId)} started; waiting for mobile messages.`;
1049
+ if (status === "already_running")
1050
+ return `${formatBotChannelName(channelId)} Bot is already running.`;
1051
+ if (status === "failed")
1052
+ return `${formatBotChannelName(channelId)} Bot start failed; check app settings.`;
1053
+ return `${formatBotChannelName(channelId)} Bot start blocked; finish setup first.`;
1054
+ }
1055
+ function selectBotChannels(context, channelArg) {
1056
+ const normalized = normalizeRemoteChannelId(channelArg ?? "");
1057
+ if (normalized) {
1058
+ const channel = findRemoteChannel(context, normalized);
1059
+ return channel ? [channel] : [];
1060
+ }
1061
+ return context.remote.channels.filter(
1062
+ (channel) => channel.id === "feishu" || channel.id === "dingtalk"
1063
+ );
1064
+ }
1065
+ function getRemoteBotUserStatus(channel) {
1066
+ const report = getRemoteBridgeDoctor(
1067
+ {
1068
+ enabled: true,
1069
+ channels: [channel],
1070
+ events: [],
1071
+ processedMessageIds: [],
1072
+ sessionDisabledChannelIds: [],
1073
+ pairings: [],
1074
+ inbox: []
1075
+ },
1076
+ channel.id
1077
+ );
1078
+ if (channel.id === "feishu") {
1079
+ const readiness = getFeishuBotStartReadiness(channel);
1080
+ if (feishuLongConnectionHandles.has(channel.id)) return "running";
1081
+ if (readiness.readiness === "needs-app-id") return "needs app id";
1082
+ if (readiness.readiness === "needs-app-secret") return "needs app secret";
1083
+ if (readiness.readiness === "needs-env") return "needs app env";
1084
+ if (readiness.readiness === "notification-only") return "notification-only";
1085
+ if (readiness.readiness === "ready-to-start") return "ready-to-start";
1086
+ if (readiness.readiness === "bound-ready") return "bound/ready";
1087
+ return "needs event subscription";
1088
+ }
1089
+ if (channel.id === "dingtalk") {
1090
+ if (!channel.config.appIdRef) return "needs client id";
1091
+ if (!channel.config.appSecretRef && !channel.config.tokenRef) return "needs client secret";
1092
+ if (report.readiness === "needs-daemon") return "needs stream permission";
1093
+ return "stream adapter not connected";
1094
+ }
1095
+ return "future/not implemented";
1096
+ }
1097
+ function formatBotChannelName(channelId) {
1098
+ if (channelId === "feishu") return "Feishu Bot";
1099
+ if (channelId === "dingtalk") return "DingTalk Bot";
1100
+ if (channelId === "wechat") return "Personal WeChat Bot";
1101
+ return `${channelId} Bot`;
1102
+ }
1103
+ function getRemoteCapabilityGrade(channel) {
1104
+ const { transport, type, inboundMode } = channel.config;
1105
+ if (transport === "webhook" || transport === "webhook_mock") {
1106
+ return {
1107
+ grade: "notification-only",
1108
+ reason: "webhook \u5355\u5411\u6295\u9012\u6458\u8981\uFF1B\u4E0D\u80FD\u63A5\u6536\u5BA1\u6279\u6216\u6D88\u606F\u56DE\u4F20"
1109
+ };
1110
+ }
1111
+ if (!inboundMode || inboundMode === "none") {
1112
+ return {
1113
+ grade: "notification-only",
1114
+ reason: "\u5B98\u65B9 CLI \u4EC5\u7528\u4E8E\u51FA\u7AD9\u901A\u77E5\uFF1BinboundMode=none \u672A\u5F00\u542F\u5165\u7AD9"
1115
+ };
1116
+ }
1117
+ if (type === "feishu" || type === "lark") {
1118
+ if (!channel.config.appIdRef || !channel.config.appSecretRef) {
1119
+ return {
1120
+ grade: "needs-app-setup",
1121
+ reason: "Feishu/Lark \u5165\u7AD9\u9700\u8981 appId/appSecret \u5F15\u7528\u548C\u4E8B\u4EF6\u8BA2\u9605\u914D\u7F6E\uFF1B\u672A\u914D\u7F6E\u4E0D\u80FD\u663E\u793A ready"
1122
+ };
1123
+ }
1124
+ return {
1125
+ grade: "full-mobile-control-capable",
1126
+ reason: "\u5B98\u65B9\u5E94\u7528\u4E8B\u4EF6/\u56DE\u8C03\u6216 CLI \u6D88\u8D39\u53EF\u63A5\u5165\u5BA1\u6279\u4E0E\u81EA\u7136\u8BED\u8A00\uFF1B\u771F\u5B9E\u624B\u673A\u5165\u7AD9\u4ECD\u9700 callback/daemon"
1127
+ };
1128
+ }
1129
+ if (type === "wecom" || type === "enterprise-wechat") {
1130
+ if (!channel.config.appIdRef && !channel.config.tokenRef) {
1131
+ return {
1132
+ grade: "needs-wecom-app",
1133
+ reason: "\u4F01\u4E1A\u5FAE\u4FE1\u5165\u7AD9\u9700\u8981\u5E94\u7528\u56DE\u8C03\u6216 CLI poll \u51ED\u8BC1\uFF1B\u672A\u914D\u7F6E\u663E\u793A needs-wecom-app"
1134
+ };
1135
+ }
1136
+ return {
1137
+ grade: "natural-language-inbound-capable",
1138
+ reason: "\u5E94\u7528\u56DE\u8C03/CLI poll \u53EF\u63A5\u6536\u81EA\u7136\u8BED\u8A00\uFF1Bwebhook \u4ECD\u4EC5\u901A\u77E5"
1139
+ };
1140
+ }
1141
+ if (!channel.config.appIdRef && !channel.config.tokenRef) {
1142
+ return {
1143
+ grade: "needs-dingtalk-app",
1144
+ reason: "\u9489\u9489\u5165\u7AD9/\u5BA1\u6279\u9700\u8981\u5E94\u7528\u6216 Stream \u914D\u7F6E\uFF1B\u672A\u914D\u7F6E\u663E\u793A needs-dingtalk-app"
1145
+ };
1146
+ }
1147
+ return {
1148
+ grade: "approval-capable",
1149
+ reason: "\u5E94\u7528/Stream \u914D\u7F6E\u540E\u53EF\u505A\u5BA1\u6279\u56DE\u4F20\uFF1B\u5B9E\u65F6\u6D88\u606F\u9700 daemon \u6216 callback"
1150
+ };
1151
+ }
1152
+ function formatRemoteSetup(channelArg, context) {
1153
+ const channel = findRemoteChannel(context, channelArg);
1154
+ if (!channel) {
1155
+ return "Remote setup\uFF1A\u8BF7\u9009\u62E9 feishu\u3001wecom \u6216 dingtalk\u3002\u793A\u4F8B\uFF1A/remote setup feishu";
1156
+ }
1157
+ const config = channel.config;
1158
+ const isWebhook = config.transport === "webhook" || config.transport === "webhook_mock";
1159
+ const grade = getRemoteCapabilityGrade(channel);
1160
+ const field = (label, ok, hint) => `- ${ok ? "[\u5DF2\u586B]" : "[\u5F85\u586B]"} ${label}${ok ? "" : ` \u2014 ${hint}`}`;
1161
+ const lines = [
1162
+ `Remote setup\uFF1A${channel.id}\uFF08\u9ED8\u8BA4\u4E0D\u81EA\u52A8\u542F\u7528\uFF1B\u53EA\u9700\u586B\u5FC5\u8981\u5B57\u6BB5\uFF0C\u65E0\u9700\u7406\u89E3\u5E95\u5C42\u673A\u5236\uFF09`,
1163
+ `- \u901A\u9053\u80FD\u529B\uFF1A${grade.grade} \u2014 ${grade.reason}`
1164
+ ];
1165
+ if (isWebhook) {
1166
+ lines.push(
1167
+ field(
1168
+ "webhook endpoint",
1169
+ Boolean(config.endpoint),
1170
+ `\u914D\u7F6E\u8131\u654F webhook \u5730\u5740\uFF08${getRemoteLoginHint(config.type)}\uFF09`
1171
+ )
1172
+ );
1173
+ lines.push(
1174
+ field(
1175
+ "signing secret \u5F15\u7528",
1176
+ Boolean(config.signingSecretRef),
1177
+ config.type === "wecom" || config.type === "enterprise-wechat" ? "\u4F01\u4E1A\u5FAE\u4FE1\u7FA4\u673A\u5668\u4EBA\u65E0\u72EC\u7ACB\u7B7E\u540D\uFF0C\u5B89\u5168\u6027\u6765\u81EA URL key\uFF08\u53EF\u7559\u7A7A\uFF09" : "\u586B\u73AF\u5883\u53D8\u91CF\u540D\uFF08\u5982 LINGHUN_REMOTE_FEISHU_SECRET\uFF09\uFF0C\u4E0D\u8981\u7C98\u8D34\u660E\u6587"
1178
+ )
1179
+ );
1180
+ } else {
1181
+ lines.push(
1182
+ field(
1183
+ "official CLI \u767B\u5F55",
1184
+ channel.transportStatus === "ready",
1185
+ getRemoteLoginHint(config.type)
1186
+ )
1187
+ );
1188
+ lines.push(
1189
+ field(
1190
+ "appIdRef/appSecretRef \u6216 tokenRef",
1191
+ Boolean(config.appIdRef || config.tokenRef) && (config.type !== "feishu" && config.type !== "lark" || Boolean(config.appSecretRef)),
1192
+ "\u586B\u73AF\u5883\u53D8\u91CF\u5F15\u7528\uFF0C\u4E0D\u586B\u660E\u6587\uFF1B\u672A\u914D\u7F6E\u65F6 bridge \u663E\u793A needs-app-setup"
1193
+ )
1194
+ );
1195
+ lines.push(
1196
+ field(
1197
+ "\u5165\u7AD9\u6A21\u5F0F inboundMode",
1198
+ Boolean(config.inboundMode && config.inboundMode !== "none"),
1199
+ "poll=CLI \u62C9\u53D6\u6D88\u606F / callback=\u5DF2\u90E8\u7F72\u56DE\u8C03\u7AEF\u70B9\uFF1Bnone \u4EC5\u51FA\u7AD9\u901A\u77E5"
1200
+ )
1201
+ );
1202
+ }
1203
+ lines.push(field("\u7ED1\u5B9A\u7528\u6237 bindingUserId", Boolean(config.bindingUserId), "\u586B\u624B\u673A\u7AEF\u53EF\u4FE1\u7528\u6237 id"));
1204
+ lines.push(
1205
+ field(
1206
+ "\u7ED1\u5B9A\u8BBE\u5907 bindingDeviceId",
1207
+ Boolean(config.bindingDeviceId),
1208
+ "\u53EF\u9009\uFF1B\u586B\u4E86\u5219\u5BA1\u6279/\u5165\u7AD9\u4F1A\u6821\u9A8C\u8BBE\u5907"
1209
+ )
1210
+ );
1211
+ lines.push(
1212
+ field(
1213
+ "\u53EF\u4FE1\u6765\u6E90 trustedSources",
1214
+ config.trustedSources.length > 0,
1215
+ "\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u53EF\u4FE1\u6765\u6E90 id\uFF0C\u5426\u5219\u901A\u9053\u4FDD\u6301 blocked"
1216
+ )
1217
+ );
1218
+ if (!isWebhook) {
1219
+ lines.push(
1220
+ field(
1221
+ "\u56DE\u8C03\u7AEF\u70B9 callbackEndpoint",
1222
+ Boolean(config.callbackEndpoint),
1223
+ "\u4EC5 inboundMode=callback \u9700\u8981\uFF1Bpoll \u6A21\u5F0F\u53EF\u7559\u7A7A"
1224
+ )
1225
+ );
1226
+ lines.push(
1227
+ field(
1228
+ "encryptKeyRef / verificationTokenRef",
1229
+ config.type === "feishu" || config.type === "lark" ? config.callbackEndpoint === "feishu-long-connection" || Boolean(config.encryptKeyRef && config.verificationTokenRef) : true,
1230
+ "Feishu/Lark \u516C\u7F51 callback \u9700\u8981\u4E8B\u4EF6\u56DE\u8C03\u6821\u9A8C\u5F15\u7528\uFF1B\u957F\u8FDE\u63A5\u4E0D\u9700\u8981"
1231
+ )
1232
+ );
1233
+ }
1234
+ lines.push(
1235
+ `- \u5F53\u524D\u72B6\u6001\uFF1Aruntime ${channel.runtimeStatus}; binding ${channel.bindingStatus}; transport ${config.transport}/${channel.transportStatus}`,
1236
+ `- \u4E0B\u4E00\u6B65\uFF1A\u8865\u9F50 [\u5F85\u586B] \u5B57\u6BB5\u540E\u8FD0\u884C /remote test ${channel.id}\uFF0C\u518D\u8FD0\u884C /remote status\uFF1B\u4E0D\u8981\u5728\u4E3B\u5C4F\u7C98\u8D34 secret/token/full endpoint\u3002`
1237
+ );
1238
+ return lines.join("\n");
1239
+ }
1240
+ function findRemoteChannel(context, channelArg) {
1241
+ const id = normalizeRemoteChannelId(channelArg ?? "");
1242
+ return context.remote.channels.find((channel) => channel.id === id || channel.config.type === id);
1243
+ }
1244
+ function normalizeRemoteChannelId(value) {
1245
+ const normalized = value.trim().toLowerCase();
1246
+ if (normalized === "lark") return "feishu";
1247
+ if (normalized === "enterprise-wechat") return "wecom";
1248
+ return normalized;
1249
+ }
1250
+ function getRemoteLoginHint(type) {
1251
+ if (type === "feishu" || type === "lark") {
1252
+ return "\u68C0\u6D4B lark-cli / feishu-cli\uFF1B\u672A\u521D\u59CB\u5316\u8BF7\u8FD0\u884C feishu-cli config init \u6216 lark-cli auth login\u3002";
1253
+ }
1254
+ if (type === "dingtalk") {
1255
+ return "\u68C0\u6D4B dws\uFF1B\u672A\u767B\u5F55\u8BF7\u8FD0\u884C dws auth login \u6216 dws device login\u3002";
1256
+ }
1257
+ return "\u68C0\u6D4B wecom-cli\uFF1B\u672A\u521D\u59CB\u5316\u8BF7\u8FD0\u884C wecom-cli init\uFF0C\u7136\u540E\u68C0\u67E5 auth/login \u72B6\u6001\u3002";
1258
+ }
1259
+ function getRemoteInstallHint(type) {
1260
+ if (type === "feishu" || type === "lark") {
1261
+ return "install lark-cli/feishu-cli, then run feishu-cli config init or lark-cli auth login";
1262
+ }
1263
+ if (type === "dingtalk") {
1264
+ return "install dws, then run dws auth login or dws device login";
1265
+ }
1266
+ return "install wecom-cli, then run wecom-cli init/auth";
1267
+ }
1268
+ function createRemoteEvent(channel, eventType, summary, refs = [], ttlMs = 10 * 60 * 1e3) {
1269
+ const now = Date.now();
1270
+ const id = `remote-${randomUUID().slice(0, 8)}`;
1271
+ return {
1272
+ id,
1273
+ channel: channel.id,
1274
+ eventType,
1275
+ createdAt: new Date(now).toISOString(),
1276
+ expiresAt: new Date(now + ttlMs).toISOString(),
1277
+ nonce: randomUUID(),
1278
+ messageId: `msg-${randomUUID().slice(0, 12)}`,
1279
+ source: channel.config.trustedSources[0] ?? "local-test",
1280
+ redactedSummary: redactRemoteSummary(summary),
1281
+ refs: refs.map((ref) => truncateDisplay(redactRemoteSummary(ref), 120)),
1282
+ status: "pending"
1283
+ };
1284
+ }
1285
+ function sendRemoteEvent(context, event) {
1286
+ const channel = context.remote.channels.find((item) => item.id === event.channel);
1287
+ const next = { ...event };
1288
+ if (!channel || channel.runtimeStatus !== "ready") {
1289
+ next.status = "failed";
1290
+ } else if (!channel.config.allowedEventTypes.includes(event.eventType)) {
1291
+ next.status = "rejected";
1292
+ } else if (channel.config.transport === "webhook" && !channel.config.endpoint) {
1293
+ next.status = "failed";
1294
+ } else {
1295
+ next.status = "sent";
1296
+ }
1297
+ context.remote.events.unshift(next);
1298
+ context.remote.events = context.remote.events.slice(0, 20);
1299
+ return next;
1300
+ }
1301
+ async function sendRemoteEventReal(context, event, depsOverride) {
1302
+ const transport = depsOverride ?? remoteTransportDeps ?? defaultRemoteTransportDeps();
1303
+ const channel = context.remote.channels.find((item) => item.id === event.channel);
1304
+ const next = { ...event };
1305
+ const finalize = (status2, detail) => {
1306
+ next.status = status2;
1307
+ next.deliveryDetail = detail;
1308
+ context.remote.events.unshift(next);
1309
+ context.remote.events = context.remote.events.slice(0, 20);
1310
+ return next;
1311
+ };
1312
+ if (!channel || channel.runtimeStatus !== "ready") {
1313
+ return finalize("failed", "remote channel is not ready");
1314
+ }
1315
+ if (!channel.config.allowedEventTypes.includes(event.eventType)) {
1316
+ return finalize("rejected", `event type ${event.eventType} is not allowed on this channel`);
1317
+ }
1318
+ if (channel.config.transport === "webhook_mock") {
1319
+ return finalize("mock", "webhook_mock diagnostic dry run \u2014 not a real remote delivery");
1320
+ }
1321
+ if (channel.config.transport === "webhook") {
1322
+ const secret = channel.config.signingSecretRef ? transport.resolveSecret(channel.config.signingSecretRef) : void 0;
1323
+ if (channel.config.signingSecretRef && !secret) {
1324
+ return finalize("failed", "signing secret reference could not be resolved");
1325
+ }
1326
+ const build = buildWebhookRequest(channel, event, secret, transport.nowMs());
1327
+ if (!build.ok) {
1328
+ return finalize("failed", "missing redacted webhook endpoint configuration");
1329
+ }
1330
+ const result2 = await deliverWebhook(build.request, transport.fetch);
1331
+ return finalize(result2.status === "sent" ? "sent" : "failed", result2.detail);
1332
+ }
1333
+ const invocation = buildOfficialCliInvocation(channel, event);
1334
+ if (!invocation.ok) {
1335
+ return finalize(
1336
+ "failed",
1337
+ invocation.reason === "missing_binding" ? "official CLI needs a bound user before sending" : "official CLI path is not configured"
1338
+ );
1339
+ }
1340
+ const result = await deliverOfficialCli(invocation.command, invocation.args, transport.runCli);
1341
+ const status = result.status === "sent" ? "sent" : result.status === "blocked" ? "blocked" : "failed";
1342
+ return finalize(status, result.detail);
1343
+ }
1344
+ function processRemoteApprovalForTest(context, event, message) {
1345
+ const decision = processRemoteApproval(context, event, message);
1346
+ context.remote.lastApproval = decision;
1347
+ return decision;
1348
+ }
1349
+ function processRemoteApproval(context, event, message) {
1350
+ const channel = context.remote.channels.find((item) => item.id === event.channel);
1351
+ const reject = (status, summary) => {
1352
+ event.status = status === "expired" ? "expired" : "rejected";
1353
+ return { status, summary, evidenceCreated: false };
1354
+ };
1355
+ if (!channel || channel.runtimeStatus !== "ready") {
1356
+ return reject("blocked", "remote channel is not ready");
1357
+ }
1358
+ if (event.eventType !== "approval_request") {
1359
+ return reject("blocked", "remote event is not an approval_request");
1360
+ }
1361
+ if (Date.parse(event.expiresAt) <= Date.now()) {
1362
+ return reject("expired", "remote approval expired");
1363
+ }
1364
+ if (context.remote.processedMessageIds.includes(message.messageId)) {
1365
+ return reject("replayed", "remote approval replayed");
1366
+ }
1367
+ if (message.messageId !== event.messageId || message.nonce !== event.nonce) {
1368
+ return reject("bad_signature", "remote approval nonce/messageId mismatch");
1369
+ }
1370
+ if (!channel.config.trustedSources.includes(message.source)) {
1371
+ return reject("unknown_source", "remote approval source is not trusted");
1372
+ }
1373
+ if (message.bindingUserId !== channel.config.bindingUserId || channel.config.bindingDeviceId && message.bindingDeviceId !== channel.config.bindingDeviceId) {
1374
+ return reject("wrong_binding", "remote approval binding mismatch");
1375
+ }
1376
+ if (!verifyRemoteSignature(channel, event, message)) {
1377
+ return reject("bad_signature", "remote approval signature check failed");
1378
+ }
1379
+ if (!context.pendingLocalApproval) {
1380
+ return reject("blocked", "no local pending approval to resume");
1381
+ }
1382
+ context.remote.processedMessageIds.unshift(message.messageId);
1383
+ context.remote.processedMessageIds = context.remote.processedMessageIds.slice(0, 50);
1384
+ event.status = message.approve ? "approved" : "rejected";
1385
+ return {
1386
+ status: message.approve ? "approved" : "rejected",
1387
+ summary: message.approve ? "remote approval validated; local permission pipeline remains the execution boundary" : "remote approval rejected by user",
1388
+ evidenceCreated: false
1389
+ };
1390
+ }
1391
+ function verifyRemoteSignature(channel, event, message) {
1392
+ if (!channel.config.signingSecretRef) {
1393
+ return isRemoteMockSignatureAllowed() && message.signature === `mock:${event.messageId}:${event.nonce}`;
1394
+ }
1395
+ return typeof message.signature === "string" && message.signature.startsWith("ref:");
1396
+ }
1397
+ function verifyRemoteInboundSignature(channel, message) {
1398
+ if (!channel.config.signingSecretRef) {
1399
+ return isRemoteMockSignatureAllowed() && message.origin === "fixture" && message.signature === `mock:inbound:${message.messageId}:${message.nonce}`;
1400
+ }
1401
+ return typeof message.signature === "string" && message.signature.startsWith("ref:");
1402
+ }
1403
+ function isRemoteMockSignatureAllowed() {
1404
+ return process.env.NODE_ENV !== "production";
1405
+ }
1406
+ function processRemoteInbound(context, message) {
1407
+ const envelope = validateRemoteInboundEnvelope(context, message);
1408
+ if (envelope.status !== "envelope_accepted") return envelope;
1409
+ const channel = envelope.channel;
1410
+ const consume = () => consumeRemoteInboundMessage(context, message.messageId);
1411
+ if (message.kind === "approval_response") {
1412
+ if (context.permissionMode === "plan") {
1413
+ return {
1414
+ kind: message.kind,
1415
+ status: "blocked",
1416
+ summary: "plan mode keeps writes read-only; remote approval cannot execute mutating operations",
1417
+ evidenceCreated: false
1418
+ };
1419
+ }
1420
+ if (!context.pendingLocalApproval) {
1421
+ return {
1422
+ kind: message.kind,
1423
+ status: "no_pending_approval",
1424
+ summary: "no local pending approval to resume",
1425
+ evidenceCreated: false
1426
+ };
1427
+ }
1428
+ const event = message.eventId ? context.remote.events.find((item) => item.id === message.eventId) : void 0;
1429
+ if (!event || event.eventType !== "approval_request") {
1430
+ return {
1431
+ kind: message.kind,
1432
+ status: "blocked",
1433
+ summary: "approval_response does not match a known approval_request",
1434
+ evidenceCreated: false
1435
+ };
1436
+ }
1437
+ if (Date.parse(event.expiresAt) <= Date.now()) {
1438
+ return {
1439
+ kind: message.kind,
1440
+ status: "expired",
1441
+ summary: "approval_request expired",
1442
+ evidenceCreated: false
1443
+ };
1444
+ }
1445
+ if (message.nonce !== event.nonce) {
1446
+ return {
1447
+ kind: message.kind,
1448
+ status: "bad_signature",
1449
+ summary: "approval_response nonce mismatch",
1450
+ evidenceCreated: false
1451
+ };
1452
+ }
1453
+ consume();
1454
+ event.status = message.approve ? "approved" : "rejected";
1455
+ return {
1456
+ kind: "approval_response",
1457
+ status: message.approve ? "approved" : "rejected",
1458
+ summary: message.approve ? "remote approval validated; local permission pipeline remains the execution boundary" : "remote approval rejected by user",
1459
+ evidenceCreated: false
1460
+ };
1461
+ }
1462
+ if (message.kind === "natural_language_message") {
1463
+ const text = (message.text ?? "").trim();
1464
+ if (!text) {
1465
+ return {
1466
+ kind: message.kind,
1467
+ status: "blocked",
1468
+ summary: "natural_language_message is empty",
1469
+ evidenceCreated: false
1470
+ };
1471
+ }
1472
+ consume();
1473
+ return {
1474
+ kind: "natural_language_message",
1475
+ status: "accepted",
1476
+ summary: "remote natural-language message accepted; routing into local model main chain",
1477
+ routedText: text,
1478
+ evidenceCreated: false
1479
+ };
1480
+ }
1481
+ void channel;
1482
+ consume();
1483
+ return {
1484
+ kind: "status_query",
1485
+ status: "accepted",
1486
+ summary: "remote status query accepted; returning redacted local status summary",
1487
+ evidenceCreated: false
1488
+ };
1489
+ }
1490
+ function validateRemoteInboundEnvelope(context, message) {
1491
+ const channel = context.remote.channels.find((item) => item.id === message.channel);
1492
+ const reject = (status, summary) => ({ kind: message.kind, status, summary, evidenceCreated: false });
1493
+ if (message.channel === "local-repl") {
1494
+ if (message.origin !== "adapter" || message.source !== "local-repl") {
1495
+ return reject("bad_signature", "local REPL bridge proof is invalid");
1496
+ }
1497
+ if (Date.parse(message.expiresAt) <= Date.now()) {
1498
+ return reject("expired", "local REPL bridge message expired");
1499
+ }
1500
+ if (context.remote.processedMessageIds.includes(message.messageId)) {
1501
+ return reject("replayed", "local REPL bridge message replayed");
1502
+ }
1503
+ const client = context.remote.localReplBridge?.clients.find(
1504
+ (item) => item.active && item.clientId === message.bindingUserId
1505
+ );
1506
+ if (!client || !client.queue.some((item) => item.messageId === message.messageId)) {
1507
+ return reject("wrong_binding", "local REPL bridge client/message mismatch");
1508
+ }
1509
+ return { status: "envelope_accepted", channel: createLocalReplBridgeChannel() };
1510
+ }
1511
+ if (!channel || !canValidateRemoteInboundEnvelope(channel)) {
1512
+ return reject("channel_not_ready", "remote channel is not ready");
1513
+ }
1514
+ if (channel.config.transport !== "official_cli" || !channel.config.inboundMode || channel.config.inboundMode === "none") {
1515
+ return reject("inbound_disabled", "remote channel is notification-only; inbound is disabled");
1516
+ }
1517
+ if (Date.parse(message.expiresAt) <= Date.now()) {
1518
+ return reject("expired", "remote inbound message expired");
1519
+ }
1520
+ if (context.remote.processedMessageIds.includes(message.messageId)) {
1521
+ return reject("replayed", "remote inbound message replayed");
1522
+ }
1523
+ if (!channel.config.trustedSources.includes(message.source)) {
1524
+ return reject("unknown_source", "remote inbound source is not trusted");
1525
+ }
1526
+ if (message.bindingUserId !== channel.config.bindingUserId || channel.config.bindingDeviceId && message.bindingDeviceId !== channel.config.bindingDeviceId) {
1527
+ return reject("wrong_binding", "remote inbound binding mismatch");
1528
+ }
1529
+ if (!verifyRemoteInboundSignature(channel, message)) {
1530
+ return reject("bad_signature", "remote inbound signature check failed");
1531
+ }
1532
+ return { status: "envelope_accepted", channel };
1533
+ }
1534
+ function createLocalReplBridgeChannel() {
1535
+ return {
1536
+ id: "local-repl",
1537
+ config: {
1538
+ enabled: true,
1539
+ type: "lark",
1540
+ transport: "official_cli",
1541
+ redactionPolicy: "summary_only",
1542
+ allowedEventTypes: [],
1543
+ trustedSources: ["local-repl"],
1544
+ inboundMode: "callback",
1545
+ bindingUserId: "local-repl"
1546
+ },
1547
+ runtimeStatus: "ready",
1548
+ bindingStatus: "bound",
1549
+ transportStatus: "ready",
1550
+ nextAction: "/remote bridge local-route"
1551
+ };
1552
+ }
1553
+ function validateRemotePairingEnvelope(context, message) {
1554
+ const channel = context.remote.channels.find((item) => item.id === message.channel);
1555
+ const reject = (status, summary) => ({ kind: message.kind, status, summary, evidenceCreated: false });
1556
+ if (!channel || !canValidateRemoteInboundEnvelope(channel)) {
1557
+ return reject("channel_not_ready", "remote channel is not ready");
1558
+ }
1559
+ if (channel.config.transport !== "official_cli" || !channel.config.inboundMode || channel.config.inboundMode === "none") {
1560
+ return reject("inbound_disabled", "remote channel is notification-only; inbound is disabled");
1561
+ }
1562
+ if (Date.parse(message.expiresAt) <= Date.now()) {
1563
+ return reject("expired", "remote pairing message expired");
1564
+ }
1565
+ if (context.remote.processedMessageIds.includes(message.messageId)) {
1566
+ return reject("replayed", "remote pairing message replayed");
1567
+ }
1568
+ if (!verifyRemoteInboundSignature(channel, message)) {
1569
+ return reject("bad_signature", "remote pairing signature check failed");
1570
+ }
1571
+ return { status: "envelope_accepted", channel };
1572
+ }
1573
+ function canValidateRemoteInboundEnvelope(channel) {
1574
+ if (channel.runtimeStatus === "ready") return true;
1575
+ if (channel.runtimeStatus !== "blocked") return false;
1576
+ if (channel.lastError !== "not_bound" && channel.lastError !== "source_not_trusted") return false;
1577
+ return channel.config.transport === "official_cli" && Boolean(channel.config.inboundMode && channel.config.inboundMode !== "none");
1578
+ }
1579
+ function consumeRemoteInboundMessage(context, messageId) {
1580
+ context.remote.processedMessageIds.unshift(messageId);
1581
+ context.remote.processedMessageIds = context.remote.processedMessageIds.slice(0, 50);
1582
+ }
1583
+ async function appendRemoteSystemEvent(context, message, level) {
1584
+ await deps().appendSystemEvent(
1585
+ context,
1586
+ await deps().ensureSession(context),
1587
+ remoteTranscriptSummary(message),
1588
+ level
1589
+ );
1590
+ }
1591
+
1592
+ export {
1593
+ configureRemoteCommandRuntime,
1594
+ configureRemoteTransport,
1595
+ refreshRemoteState,
1596
+ handleRemoteCommand,
1597
+ formatRemoteEvents,
1598
+ formatRemoteDoctor,
1599
+ getRemoteCapabilityGrade,
1600
+ formatRemoteSetup,
1601
+ findRemoteChannel,
1602
+ normalizeRemoteChannelId,
1603
+ getRemoteLoginHint,
1604
+ getRemoteInstallHint,
1605
+ createRemoteEvent,
1606
+ sendRemoteEvent,
1607
+ sendRemoteEventReal,
1608
+ processRemoteApprovalForTest,
1609
+ processRemoteApproval,
1610
+ verifyRemoteSignature,
1611
+ verifyRemoteInboundSignature,
1612
+ processRemoteInbound,
1613
+ validateRemoteInboundEnvelope,
1614
+ validateRemotePairingEnvelope,
1615
+ consumeRemoteInboundMessage,
1616
+ appendRemoteSystemEvent
1617
+ };