@songsid/agend 2.3.4-beta.7 → 2.3.5-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. package/README.md +2 -0
  2. package/README.zh-TW.md +2 -0
  3. package/THIRD_PARTY_NOTICES.md +53 -0
  4. package/dist/agent-endpoint.d.ts +5 -1
  5. package/dist/agent-endpoint.js +12 -2
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/auth-api.d.ts +21 -0
  8. package/dist/auth-api.js +89 -2
  9. package/dist/auth-api.js.map +1 -1
  10. package/dist/authority/web-authority.d.ts +12 -2
  11. package/dist/authority/web-authority.js +25 -4
  12. package/dist/authority/web-authority.js.map +1 -1
  13. package/dist/backend/claude-code.d.ts +15 -12
  14. package/dist/backend/claude-code.js +152 -133
  15. package/dist/backend/claude-code.js.map +1 -1
  16. package/dist/backend/codex-package.d.ts +5 -2
  17. package/dist/backend/codex-package.js +1 -5
  18. package/dist/backend/codex-package.js.map +1 -1
  19. package/dist/backend/codex.d.ts +136 -113
  20. package/dist/backend/codex.js +1281 -1219
  21. package/dist/backend/codex.js.map +1 -1
  22. package/dist/backend/grok-model-cache.d.ts +7 -0
  23. package/dist/backend/grok-model-cache.js +65 -0
  24. package/dist/backend/grok-model-cache.js.map +1 -0
  25. package/dist/backend/grok.d.ts +2 -0
  26. package/dist/backend/grok.js +22 -0
  27. package/dist/backend/grok.js.map +1 -1
  28. package/dist/backend/native-pane.d.ts +9 -0
  29. package/dist/backend/native-pane.js +59 -0
  30. package/dist/backend/native-pane.js.map +1 -0
  31. package/dist/backend/types.d.ts +7 -0
  32. package/dist/backend/types.js.map +1 -1
  33. package/dist/channel/ipc-bridge.d.ts +2 -0
  34. package/dist/channel/mcp-tools.js +1 -1
  35. package/dist/channel/mcp-tools.js.map +1 -1
  36. package/dist/cli.js +3 -0
  37. package/dist/cli.js.map +1 -1
  38. package/dist/config-validator.js +71 -1
  39. package/dist/config-validator.js.map +1 -1
  40. package/dist/config.js +16 -0
  41. package/dist/config.js.map +1 -1
  42. package/dist/daemon.d.ts +67 -5
  43. package/dist/daemon.js +823 -266
  44. package/dist/daemon.js.map +1 -1
  45. package/dist/delivery-consumed-watch.d.ts +15 -3
  46. package/dist/delivery-consumed-watch.js +23 -1
  47. package/dist/delivery-consumed-watch.js.map +1 -1
  48. package/dist/delivery-outbox.d.ts +5 -0
  49. package/dist/delivery-outbox.js +32 -4
  50. package/dist/delivery-outbox.js.map +1 -1
  51. package/dist/delivery-reconciliation.d.ts +2 -35
  52. package/dist/delivery-reconciliation.js +2 -156
  53. package/dist/delivery-reconciliation.js.map +1 -1
  54. package/dist/fleet-context.d.ts +2 -2
  55. package/dist/fleet-level-config.d.ts +1 -1
  56. package/dist/fleet-level-config.js +2 -0
  57. package/dist/fleet-level-config.js.map +1 -1
  58. package/dist/fleet-manager.d.ts +238 -3
  59. package/dist/fleet-manager.js +1826 -104
  60. package/dist/fleet-manager.js.map +1 -1
  61. package/dist/fleet-windows-runtime.d.ts +92 -0
  62. package/dist/fleet-windows-runtime.js +380 -0
  63. package/dist/fleet-windows-runtime.js.map +1 -0
  64. package/dist/general-binding.d.ts +85 -0
  65. package/dist/general-binding.js +298 -0
  66. package/dist/general-binding.js.map +1 -0
  67. package/dist/general-host-owner.d.ts +8 -0
  68. package/dist/general-host-owner.js +26 -0
  69. package/dist/general-host-owner.js.map +1 -0
  70. package/dist/general-instance.d.ts +7 -0
  71. package/dist/general-instance.js +20 -3
  72. package/dist/general-instance.js.map +1 -1
  73. package/dist/general-role.d.ts +5 -0
  74. package/dist/general-role.js +26 -0
  75. package/dist/general-role.js.map +1 -0
  76. package/dist/instance-config-impact.js +4 -0
  77. package/dist/instance-config-impact.js.map +1 -1
  78. package/dist/instance-lifecycle.d.ts +21 -3
  79. package/dist/instance-lifecycle.js +378 -162
  80. package/dist/instance-lifecycle.js.map +1 -1
  81. package/dist/instructions.d.ts +2 -0
  82. package/dist/instructions.js +6 -0
  83. package/dist/instructions.js.map +1 -1
  84. package/dist/local-pane-endpoint.d.ts +46 -0
  85. package/dist/local-pane-endpoint.js +189 -0
  86. package/dist/local-pane-endpoint.js.map +1 -0
  87. package/dist/local-pane-launcher.d.ts +2 -0
  88. package/dist/local-pane-launcher.js +38 -0
  89. package/dist/local-pane-launcher.js.map +1 -0
  90. package/dist/local-pane-stage.ps1 +101 -0
  91. package/dist/locale.js +85 -0
  92. package/dist/locale.js.map +1 -1
  93. package/dist/model-catalog-defaults.d.ts +14 -0
  94. package/dist/model-catalog-defaults.js +20 -0
  95. package/dist/model-catalog-defaults.js.map +1 -0
  96. package/dist/native-agent-admission.d.ts +7 -0
  97. package/dist/native-agent-admission.js +26 -0
  98. package/dist/native-agent-admission.js.map +1 -0
  99. package/dist/native-daemon-client.d.ts +13 -0
  100. package/dist/native-daemon-client.js +28 -0
  101. package/dist/native-daemon-client.js.map +1 -0
  102. package/dist/native-instance-api.d.ts +14 -0
  103. package/dist/native-instance-api.js +205 -0
  104. package/dist/native-instance-api.js.map +1 -0
  105. package/dist/native-instance-input.d.ts +84 -0
  106. package/dist/native-instance-input.js +54 -0
  107. package/dist/native-instance-input.js.map +1 -0
  108. package/dist/native-instance-registry.d.ts +32 -0
  109. package/dist/native-instance-registry.js +105 -0
  110. package/dist/native-instance-registry.js.map +1 -0
  111. package/dist/native-mcp.d.ts +40 -0
  112. package/dist/native-mcp.js +278 -0
  113. package/dist/native-mcp.js.map +1 -0
  114. package/dist/native-model-catalog.d.ts +5 -0
  115. package/dist/native-model-catalog.js +76 -0
  116. package/dist/native-model-catalog.js.map +1 -0
  117. package/dist/native-pane-crash-budget.d.ts +20 -0
  118. package/dist/native-pane-crash-budget.js +60 -0
  119. package/dist/native-pane-crash-budget.js.map +1 -0
  120. package/dist/native-pane-create.d.ts +26 -0
  121. package/dist/native-pane-create.js +53 -0
  122. package/dist/native-pane-create.js.map +1 -0
  123. package/dist/native-pane-lifecycle.d.ts +65 -0
  124. package/dist/native-pane-lifecycle.js +230 -0
  125. package/dist/native-pane-lifecycle.js.map +1 -0
  126. package/dist/native-pane-manager.d.ts +59 -0
  127. package/dist/native-pane-manager.js +584 -0
  128. package/dist/native-pane-manager.js.map +1 -0
  129. package/dist/native-pane-output-log.d.ts +15 -0
  130. package/dist/native-pane-output-log.js +108 -0
  131. package/dist/native-pane-output-log.js.map +1 -0
  132. package/dist/native-pane-reconciliation.d.ts +4 -0
  133. package/dist/native-pane-reconciliation.js +40 -0
  134. package/dist/native-pane-reconciliation.js.map +1 -0
  135. package/dist/native-pane-recovery.d.ts +12 -0
  136. package/dist/native-pane-recovery.js +10 -0
  137. package/dist/native-pane-recovery.js.map +1 -0
  138. package/dist/native-pane-tools.d.ts +2 -0
  139. package/dist/native-pane-tools.js +14 -0
  140. package/dist/native-pane-tools.js.map +1 -0
  141. package/dist/needs-you-hub.d.ts +2 -0
  142. package/dist/needs-you-hub.js +2 -1
  143. package/dist/needs-you-hub.js.map +1 -1
  144. package/dist/needs-you.d.ts +19 -1
  145. package/dist/needs-you.js +12 -2
  146. package/dist/needs-you.js.map +1 -1
  147. package/dist/outbound-handlers.d.ts +16 -2
  148. package/dist/outbound-handlers.js +67 -3
  149. package/dist/outbound-handlers.js.map +1 -1
  150. package/dist/outbound-schemas.d.ts +38 -0
  151. package/dist/outbound-schemas.js +12 -0
  152. package/dist/outbound-schemas.js.map +1 -1
  153. package/dist/pane-port.d.ts +118 -0
  154. package/dist/pane-port.js +2 -0
  155. package/dist/pane-port.js.map +1 -0
  156. package/dist/public-web-gateway.js +9 -0
  157. package/dist/public-web-gateway.js.map +1 -1
  158. package/dist/public-web-link.d.ts +5 -0
  159. package/dist/public-web-link.js +4 -0
  160. package/dist/public-web-link.js.map +1 -1
  161. package/dist/quickstart-api.js +9 -5
  162. package/dist/quickstart-api.js.map +1 -1
  163. package/dist/quickstart.d.ts +1 -0
  164. package/dist/quickstart.js +4 -1
  165. package/dist/quickstart.js.map +1 -1
  166. package/dist/runtime-api.d.ts +8 -0
  167. package/dist/runtime-api.js +96 -0
  168. package/dist/runtime-api.js.map +1 -0
  169. package/dist/runtime-assets/@xterm/addon-serialize/LICENSE +21 -0
  170. package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.js +2 -0
  171. package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.mjs +19 -0
  172. package/dist/runtime-assets/@xterm/addon-serialize/package.json +29 -0
  173. package/dist/runtime-assets/@xterm/headless/LICENSE +21 -0
  174. package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.js +2 -0
  175. package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
  176. package/dist/runtime-assets/@xterm/headless/package.json +29 -0
  177. package/dist/runtime-assets/node-pty/CONPTY-LICENSE +21 -0
  178. package/dist/runtime-assets/node-pty/CONPTY-NOTICE.md +503 -0
  179. package/dist/runtime-assets/node-pty/LICENSE +69 -0
  180. package/dist/runtime-assets/node-pty/lib/conpty_console_list_agent.js +16 -0
  181. package/dist/runtime-assets/node-pty/lib/eventEmitter2.js +47 -0
  182. package/dist/runtime-assets/node-pty/lib/index.js +52 -0
  183. package/dist/runtime-assets/node-pty/lib/interfaces.js +7 -0
  184. package/dist/runtime-assets/node-pty/lib/shared/conout.js +11 -0
  185. package/dist/runtime-assets/node-pty/lib/terminal.js +190 -0
  186. package/dist/runtime-assets/node-pty/lib/types.js +7 -0
  187. package/dist/runtime-assets/node-pty/lib/unixTerminal.js +346 -0
  188. package/dist/runtime-assets/node-pty/lib/utils.js +39 -0
  189. package/dist/runtime-assets/node-pty/lib/windowsConoutConnection.js +125 -0
  190. package/dist/runtime-assets/node-pty/lib/windowsPtyAgent.js +320 -0
  191. package/dist/runtime-assets/node-pty/lib/windowsTerminal.js +199 -0
  192. package/dist/runtime-assets/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  193. package/dist/runtime-assets/node-pty/package.json +64 -0
  194. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  195. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  196. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  197. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  198. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  199. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  200. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  201. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  202. package/dist/runtime-bundle.d.ts +17 -0
  203. package/dist/runtime-bundle.js +150 -0
  204. package/dist/runtime-bundle.js.map +1 -0
  205. package/dist/runtime-cli.d.ts +2 -0
  206. package/dist/runtime-cli.js +117 -0
  207. package/dist/runtime-cli.js.map +1 -0
  208. package/dist/runtime-client-store.d.ts +49 -0
  209. package/dist/runtime-client-store.js +292 -0
  210. package/dist/runtime-client-store.js.map +1 -0
  211. package/dist/runtime-client.d.ts +100 -0
  212. package/dist/runtime-client.js +700 -0
  213. package/dist/runtime-client.js.map +1 -0
  214. package/dist/runtime-coordinator.d.ts +82 -0
  215. package/dist/runtime-coordinator.js +864 -0
  216. package/dist/runtime-coordinator.js.map +1 -0
  217. package/dist/runtime-general-protocol.d.ts +22 -0
  218. package/dist/runtime-general-protocol.js +57 -0
  219. package/dist/runtime-general-protocol.js.map +1 -0
  220. package/dist/runtime-general-workspace.d.ts +17 -0
  221. package/dist/runtime-general-workspace.js +213 -0
  222. package/dist/runtime-general-workspace.js.map +1 -0
  223. package/dist/runtime-pane-assets.d.ts +17 -0
  224. package/dist/runtime-pane-assets.js +23 -0
  225. package/dist/runtime-pane-assets.js.map +1 -0
  226. package/dist/runtime-pane-boot.d.ts +2 -0
  227. package/dist/runtime-pane-boot.js +24 -0
  228. package/dist/runtime-pane-boot.js.map +1 -0
  229. package/dist/runtime-pane-codex-index.d.ts +1 -0
  230. package/dist/runtime-pane-codex-index.js +47 -0
  231. package/dist/runtime-pane-codex-index.js.map +1 -0
  232. package/dist/runtime-pane-config.d.ts +16 -0
  233. package/dist/runtime-pane-config.js +54 -0
  234. package/dist/runtime-pane-config.js.map +1 -0
  235. package/dist/runtime-pane-host.d.ts +44 -0
  236. package/dist/runtime-pane-host.js +424 -0
  237. package/dist/runtime-pane-host.js.map +1 -0
  238. package/dist/runtime-pane-job-owner.d.ts +1 -0
  239. package/dist/runtime-pane-job-owner.js +29 -0
  240. package/dist/runtime-pane-job-owner.js.map +1 -0
  241. package/dist/runtime-pane-job.ps1 +116 -0
  242. package/dist/runtime-pane-link.d.ts +40 -0
  243. package/dist/runtime-pane-link.js +246 -0
  244. package/dist/runtime-pane-link.js.map +1 -0
  245. package/dist/runtime-pane-mcp-protocol.d.ts +32 -0
  246. package/dist/runtime-pane-mcp-protocol.js +39 -0
  247. package/dist/runtime-pane-mcp-protocol.js.map +1 -0
  248. package/dist/runtime-pane-mcp-relay.d.ts +17 -0
  249. package/dist/runtime-pane-mcp-relay.js +70 -0
  250. package/dist/runtime-pane-mcp-relay.js.map +1 -0
  251. package/dist/runtime-pane-mcp.d.ts +51 -0
  252. package/dist/runtime-pane-mcp.js +150 -0
  253. package/dist/runtime-pane-mcp.js.map +1 -0
  254. package/dist/runtime-pane-native.d.ts +45 -0
  255. package/dist/runtime-pane-native.js +65 -0
  256. package/dist/runtime-pane-native.js.map +1 -0
  257. package/dist/runtime-pane-output.d.ts +19 -0
  258. package/dist/runtime-pane-output.js +53 -0
  259. package/dist/runtime-pane-output.js.map +1 -0
  260. package/dist/runtime-pane-port.d.ts +139 -0
  261. package/dist/runtime-pane-port.js +558 -0
  262. package/dist/runtime-pane-port.js.map +1 -0
  263. package/dist/runtime-pane-private.d.ts +14 -0
  264. package/dist/runtime-pane-private.js +51 -0
  265. package/dist/runtime-pane-private.js.map +1 -0
  266. package/dist/runtime-pane-private.ps1 +14 -0
  267. package/dist/runtime-pane-process.d.ts +4 -0
  268. package/dist/runtime-pane-process.js +108 -0
  269. package/dist/runtime-pane-process.js.map +1 -0
  270. package/dist/runtime-pane-protocol.d.ts +172 -0
  271. package/dist/runtime-pane-protocol.js +195 -0
  272. package/dist/runtime-pane-protocol.js.map +1 -0
  273. package/dist/runtime-pane-screen.d.ts +19 -0
  274. package/dist/runtime-pane-screen.js +76 -0
  275. package/dist/runtime-pane-screen.js.map +1 -0
  276. package/dist/runtime-pane-service.d.ts +11 -0
  277. package/dist/runtime-pane-service.js +221 -0
  278. package/dist/runtime-pane-service.js.map +1 -0
  279. package/dist/runtime-pane-stdio-protocol.d.ts +54 -0
  280. package/dist/runtime-pane-stdio-protocol.js +94 -0
  281. package/dist/runtime-pane-stdio-protocol.js.map +1 -0
  282. package/dist/runtime-pane-stdio.d.ts +3 -0
  283. package/dist/runtime-pane-stdio.js +123 -0
  284. package/dist/runtime-pane-stdio.js.map +1 -0
  285. package/dist/runtime-pane-store.d.ts +33 -0
  286. package/dist/runtime-pane-store.js +164 -0
  287. package/dist/runtime-pane-store.js.map +1 -0
  288. package/dist/runtime-pane-transcript-protocol.d.ts +43 -0
  289. package/dist/runtime-pane-transcript-protocol.js +58 -0
  290. package/dist/runtime-pane-transcript-protocol.js.map +1 -0
  291. package/dist/runtime-pane-transcript.d.ts +45 -0
  292. package/dist/runtime-pane-transcript.js +605 -0
  293. package/dist/runtime-pane-transcript.js.map +1 -0
  294. package/dist/runtime-pane-worker.d.ts +1 -0
  295. package/dist/runtime-pane-worker.js +154 -0
  296. package/dist/runtime-pane-worker.js.map +1 -0
  297. package/dist/runtime-protocol.d.ts +149 -0
  298. package/dist/runtime-protocol.js +19 -0
  299. package/dist/runtime-protocol.js.map +1 -0
  300. package/dist/runtime-switches.d.ts +79 -0
  301. package/dist/runtime-switches.js +90 -0
  302. package/dist/runtime-switches.js.map +1 -0
  303. package/dist/runtime-transcript-delta-reader.d.ts +23 -0
  304. package/dist/runtime-transcript-delta-reader.js +74 -0
  305. package/dist/runtime-transcript-delta-reader.js.map +1 -0
  306. package/dist/runtime-transcript-source.d.ts +36 -0
  307. package/dist/runtime-transcript-source.js +189 -0
  308. package/dist/runtime-transcript-source.js.map +1 -0
  309. package/dist/runtime-windows-bridge.d.ts +41 -0
  310. package/dist/runtime-windows-bridge.js +157 -0
  311. package/dist/runtime-windows-bridge.js.map +1 -0
  312. package/dist/settings-api.d.ts +5 -0
  313. package/dist/settings-api.js +95 -1
  314. package/dist/settings-api.js.map +1 -1
  315. package/dist/settings-baseline.d.ts +1 -0
  316. package/dist/settings-baseline.js +1 -1
  317. package/dist/settings-baseline.js.map +1 -1
  318. package/dist/settings-change.js +28 -0
  319. package/dist/settings-change.js.map +1 -1
  320. package/dist/settings-confirmation.d.ts +16 -1
  321. package/dist/settings-confirmation.js +47 -9
  322. package/dist/settings-confirmation.js.map +1 -1
  323. package/dist/settings-effect.d.ts +4 -0
  324. package/dist/settings-effect.js +58 -1
  325. package/dist/settings-effect.js.map +1 -1
  326. package/dist/settings-http-confirmation.d.ts +1 -0
  327. package/dist/settings-http-confirmation.js +15 -2
  328. package/dist/settings-http-confirmation.js.map +1 -1
  329. package/dist/settings-request-capability.d.ts +2 -0
  330. package/dist/settings-request-capability.js +4 -0
  331. package/dist/settings-request-capability.js.map +1 -1
  332. package/dist/settings-route-lifetime.d.ts +14 -0
  333. package/dist/settings-route-lifetime.js +48 -0
  334. package/dist/settings-route-lifetime.js.map +1 -0
  335. package/dist/settings-transaction.d.ts +4 -0
  336. package/dist/settings-transaction.js +9 -0
  337. package/dist/settings-transaction.js.map +1 -1
  338. package/dist/setup-form.js +6 -3
  339. package/dist/setup-form.js.map +1 -1
  340. package/dist/setup-i18n.js +3 -0
  341. package/dist/setup-i18n.js.map +1 -1
  342. package/dist/tmux-control.d.ts +2 -1
  343. package/dist/tmux-control.js.map +1 -1
  344. package/dist/tmux-manager.d.ts +2 -1
  345. package/dist/tmux-manager.js.map +1 -1
  346. package/dist/tmux-pane-host.d.ts +26 -0
  347. package/dist/tmux-pane-host.js +25 -0
  348. package/dist/tmux-pane-host.js.map +1 -0
  349. package/dist/tool-permissions.js +2 -1
  350. package/dist/tool-permissions.js.map +1 -1
  351. package/dist/topic-commands.js +3 -0
  352. package/dist/topic-commands.js.map +1 -1
  353. package/dist/transcript-delta-reader.d.ts +1 -1
  354. package/dist/transcript-delta-reader.js +1 -1
  355. package/dist/transcript-delta-reader.js.map +1 -1
  356. package/dist/transcript-monitor.d.ts +3 -0
  357. package/dist/transcript-monitor.js +22 -46
  358. package/dist/transcript-monitor.js.map +1 -1
  359. package/dist/transcript-needles.d.ts +35 -0
  360. package/dist/transcript-needles.js +158 -0
  361. package/dist/transcript-needles.js.map +1 -0
  362. package/dist/transcript-records.d.ts +21 -0
  363. package/dist/transcript-records.js +140 -0
  364. package/dist/transcript-records.js.map +1 -0
  365. package/dist/transcript-sources.d.ts +5 -0
  366. package/dist/transcript-sources.js +8 -79
  367. package/dist/transcript-sources.js.map +1 -1
  368. package/dist/types.d.ts +23 -0
  369. package/dist/ui/agent-proposals.js +90 -0
  370. package/dist/ui/agent-runtime.js +106 -0
  371. package/dist/ui/chat-store.js +13 -2
  372. package/dist/ui/chat-strings.js +48 -0
  373. package/dist/ui/cli-picker.js +42 -0
  374. package/dist/ui/general-location.js +71 -0
  375. package/dist/ui/native-instance-settings.js +47 -0
  376. package/dist/ui/native-pane-recovery.js +45 -0
  377. package/dist/ui/panel-chat.js +63 -32
  378. package/dist/ui/panel-details.js +2 -1
  379. package/dist/ui/panel-fleet.js +99 -18
  380. package/dist/ui/panel-needs.js +154 -3
  381. package/dist/ui/panel-runtimes.js +231 -0
  382. package/dist/ui/panel-settings.js +19 -9
  383. package/dist/ui/panel-windows-agent.js +86 -0
  384. package/dist/ui/panel-windows-cli.js +143 -0
  385. package/dist/ui/provider-marks.js +83 -0
  386. package/dist/ui/runtime-catalog.js +55 -0
  387. package/dist/ui/settings-catalog.js +37 -10
  388. package/dist/ui/settings-confirm.js +3 -1
  389. package/dist/ui/settings-dialogs.js +22 -7
  390. package/dist/ui/settings-model.js +2 -1
  391. package/dist/ui/settings-outside.js +176 -0
  392. package/dist/ui/settings-registry.js +6 -0
  393. package/dist/ui/settings-strings.js +16 -0
  394. package/dist/ui/shared/app-i18n.js +2 -2
  395. package/dist/ui/shared/app-route.js +6 -2
  396. package/dist/ui/shared/app-shell.js +3 -2
  397. package/dist/ui/shared/app-store.js +1 -0
  398. package/dist/ui/shared/app-stream.js +3 -1
  399. package/dist/ui/shared/app.css +301 -0
  400. package/dist/ui/shared/app.js +4 -0
  401. package/dist/ui/shared/header-tools.js +6 -4
  402. package/dist/ui/shared/instance-nav.js +33 -7
  403. package/dist/ui/shared/instance-switch.js +4 -3
  404. package/dist/ui/shared/qrcode.module.js +2237 -0
  405. package/dist/ui/shared/signin.js +39 -5
  406. package/dist/ui/shared/ui-menu.js +28 -8
  407. package/dist/ui/shared/vendor/qrcode-generator.LICENSE +27 -0
  408. package/dist/ui/shared/vendor/vendor.json +19 -2
  409. package/dist/ui/shared/view-strings.js +8 -0
  410. package/dist/ui/windows-agents-nav.js +2 -0
  411. package/dist/ui/windows-cli-catalog.js +79 -0
  412. package/dist/web-api.d.ts +37 -0
  413. package/dist/web-api.js +248 -6
  414. package/dist/web-api.js.map +1 -1
  415. package/dist/web-chat-history.d.ts +19 -2
  416. package/dist/web-chat-history.js +72 -3
  417. package/dist/web-chat-history.js.map +1 -1
  418. package/dist/web-only-fleet.d.ts +20 -0
  419. package/dist/web-only-fleet.js +21 -0
  420. package/dist/web-only-fleet.js.map +1 -1
  421. package/dist/web-remote-access.d.ts +152 -0
  422. package/dist/web-remote-access.js +368 -0
  423. package/dist/web-remote-access.js.map +1 -0
  424. package/dist/web-session.d.ts +7 -0
  425. package/dist/web-session.js +2 -0
  426. package/dist/web-session.js.map +1 -1
  427. package/dist/web-shell-routes.d.ts +4 -1
  428. package/dist/web-shell-routes.js +7 -2
  429. package/dist/web-shell-routes.js.map +1 -1
  430. package/dist/windows-agents.d.ts +124 -0
  431. package/dist/windows-agents.js +487 -0
  432. package/dist/windows-agents.js.map +1 -0
  433. package/dist/windows-cli-api.d.ts +23 -0
  434. package/dist/windows-cli-api.js +158 -0
  435. package/dist/windows-cli-api.js.map +1 -0
  436. package/dist/windows-cli-providers.d.ts +115 -0
  437. package/dist/windows-cli-providers.js +429 -0
  438. package/dist/windows-cli-providers.js.map +1 -0
  439. package/dist/windows-cli-worker.d.ts +58 -0
  440. package/dist/windows-cli-worker.js +479 -0
  441. package/dist/windows-cli-worker.js.map +1 -0
  442. package/dist/windows-cli.d.ts +115 -0
  443. package/dist/windows-cli.js +402 -0
  444. package/dist/windows-cli.js.map +1 -0
  445. package/dist/windows-native-mcp.d.ts +19 -0
  446. package/dist/windows-native-mcp.js +143 -0
  447. package/dist/windows-native-mcp.js.map +1 -0
  448. package/dist/windows-pane-mcp.d.ts +26 -0
  449. package/dist/windows-pane-mcp.js +187 -0
  450. package/dist/windows-pane-mcp.js.map +1 -0
  451. package/package.json +9 -3
  452. package/templates/runtime-assets/conpty-LICENSE +21 -0
  453. package/templates/runtime-assets/conpty-NOTICE.md +503 -0
  454. package/templates/runtime-assets/local-pane-stage.ps1 +101 -0
  455. package/templates/runtime-assets/runtime-pane-job.ps1 +116 -0
  456. package/templates/runtime-assets/runtime-pane-private.ps1 +14 -0
  457. package/templates/runtime-assets/xterm-LICENSE +21 -0
@@ -0,0 +1,71 @@
1
+ // The location is a separate revision-bound Settings operation, never an agent-config patch.
2
+ import { html, useEffect, useRef, useState } from "/assets/app-html.js";
3
+ import { t, register } from "/assets/app-i18n.js";
4
+ import { appStore, useStore, guestPage } from "/assets/app-store.js";
5
+ import { useLease } from "/assets/app-ctx.js";
6
+ import { toast } from "/assets/ui-toast.js";
7
+ import { confirmedWrite, newKey } from "./settings-confirm.js";
8
+ import { ModelSelect } from "./settings-dialogs.js";
9
+ register("generalLocation", {
10
+ en: {
11
+ label: "Run General on", host: "This AgEnD host", windows: "Paired Windows PC",
12
+ offline: "General’s Windows PC is offline. AgEnD is still running here. Reconnect that PC, or switch General back to this host in Settings. Pending or uncertain requests will not be sent again automatically.",
13
+ exit: "General was not switched: the previous process has not been confirmed stopped. Reconnect its PC or use the existing stopped-process recovery, then try again. No replacement was started.",
14
+ start: "General’s location was saved, but it could not start. Check Needs you and choose Start or switch back. Its previous conversation is kept; no input was replayed.",
15
+ history: "Host and Windows conversations stay separate. Switching does not copy or merge their history.",
16
+ },
17
+ "zh-TW": {
18
+ label: "General 執行位置", host: "這台 AgEnD 主機", windows: "已配對的 Windows 電腦",
19
+ offline: "General 所在的 Windows 電腦已離線。AgEnD 仍在這台主機運作。請重新連線那台電腦,或到設定把 General 切回這台主機。待處理或結果不明的請求不會自動重送。",
20
+ exit: "General 尚未切換:還無法確認前一個程序已停止。請重新連線那台電腦,或完成現有的程序停止復原,再重試。沒有啟動替代程序。",
21
+ start: "General 的執行位置已儲存,但未能啟動。請查看「等你處理」,選擇啟動或切回原位置。先前的對話已保留,沒有重送輸入。",
22
+ history: "主機與 Windows 各自保留對話,切換時不會複製或合併對話紀錄。",
23
+ },
24
+ });
25
+ const tr = key => t(`generalLocation.${key}`);
26
+ export function GeneralLocation({ name, attentionOnly = false }) {
27
+ const app = useStore(appStore);
28
+ const attentionKey = JSON.stringify((app.needs || []).filter(item => item.generalLocation && (!name || item.instance === name)).map(item => item.id));
29
+ const lease = useLease("general-location"), [data, setData] = useState(null), [error, setError] = useState(null);
30
+ const read = async () => {
31
+ try { const response = await lease.fetch("/api/settings/general-location"); const result = await response.json();
32
+ if (lease.current()) { if (response.ok) { setData(result); setError(null); } else setError(result.error); }
33
+ } catch (err) { if (lease.current()) setError(err.message); }
34
+ };
35
+ useEffect(() => { if (!guestPage()) void read(); }, [name, attentionKey]);
36
+ if (guestPage()) return null;
37
+ return html`<section id="general-location" class="card general-location"><h2>${tr("label")}</h2>
38
+ ${error ? html`<p role="alert">${error}</p>` : null}
39
+ ${(data?.generals || []).filter(row => !name || row.name === name).map(row => html`<${LocationRow} key=${row.name + row.revision} row=${row} machines=${data.machines || []} attentionOnly=${attentionOnly} refresh=${read} />`)}
40
+ ${!attentionOnly ? html`<button type="button" class="btn btn-sm" onClick=${read}>${t("settings.refresh")}</button>` : null}
41
+ </section>`;
42
+ }
43
+ function LocationRow({ row, machines, attentionOnly, refresh }) {
44
+ const [target, setTarget] = useState("host"), [cli, setCli] = useState("claude-code"), [model, setModel] = useState("");
45
+ const [busy, setBusy] = useState(false), flight = useRef(false);
46
+ const lease = useLease("general-location-row");
47
+ const machine = machines.find(item => item.id === target);
48
+ // The rendered revision stays with this click. Never re-read and silently retry a stale proposal.
49
+ const propose = async next => {
50
+ if (flight.current || !lease.current()) return; flight.current = true; setBusy(true);
51
+ try { const result = await confirmedWrite("/api/settings/general-location", { body: { name: row.name, revision: row.revision, target: next }, key: newKey("general"), label: tr("label") });
52
+ if (lease.current()) { toast(result.body?.message || result.body?.error || t(result.ok ? "app.saved" : "app.failed"), result.ok); await refresh(); }
53
+ } catch (err) { if (lease.current()) toast(err.message, false); }
54
+ finally { flight.current = false; if (lease.current()) setBusy(false); }
55
+ };
56
+ if (attentionOnly && !row.offline && !row.hold) return null;
57
+ return html`<div class="general-location-row" data-general=${row.name}>
58
+ <strong>${row.displayName}</strong><p>${row.location === "host" ? tr("host") : `${machines.find(item => item.id === row.machineId)?.name || tr("windows")} · ${row.cli}`}</p>
59
+ ${row.offline || row.hold ? html`<p role="alert">${tr(row.hold || "offline")}</p>` : null}
60
+ <p class="note">${tr("history")}</p>
61
+ ${row.canSwitchBack ? html`<button type="button" class="btn" disabled=${busy || row.switching} onClick=${() => propose({ location: "host" })}>${tr("host")}</button>` : null}
62
+ ${row.hold === "start" ? html`<button type="button" class="btn" disabled=${busy || row.switching} onClick=${() => propose(row.retryTarget || (row.location === "host" ? { location: "host" } : { location: "windows", machineId: row.machineId, cli: row.cli, model: row.model }))}>${t("chat.start")}</button>` : null}
63
+ ${!attentionOnly ? html`<label>${tr("label")}<select value=${target} disabled=${busy || row.switching} onChange=${e => { setTarget(e.target.value); setModel(""); }}>
64
+ <option value="host">${tr("host")}</option>${machines.map(item => html`<option value=${item.id} disabled=${!item.online}>${item.name}</option>`)}
65
+ </select></label>
66
+ ${target !== "host" ? html`<label>${t("settings.backend")}<select value=${cli} onChange=${e => { setCli(e.target.value); setModel(""); }}>
67
+ ${["claude-code", "codex"].map(id => html`<option value=${id} disabled=${!machine?.clis.some(item => item.id === id)}>${id}</option>`)}
68
+ </select></label><${ModelSelect} value=${model} onChange=${setModel} backend=${cli} runtime="windows" machineId=${target} instance=${row.name} />` : null}
69
+ <button type="button" class="btn btn-primary" disabled=${busy || row.switching || target === "host" && !row.canSwitchBack || target !== "host" && (!machine?.online || !machine.clis.some(item => item.id === cli))} onClick=${() => propose(target === "host" ? { location: "host" } : { location: "windows", machineId: target, cli, model })}>${t("settings.save")}</button>` : null}
70
+ </div>`;
71
+ }
@@ -0,0 +1,47 @@
1
+ // Windows uses the same staged instance settings flow, with its runtime's supported fields.
2
+ import { html, useState } from "/assets/app-html.js";
3
+ import { t, register } from "/assets/app-i18n.js";
4
+ import { Dialog } from "/assets/ui-dialog.js";
5
+ import { ChipList, ModelSelect } from "./settings-dialogs.js";
6
+ import { CliProductMark, cliLabel } from "./cli-picker.js";
7
+ import { useWindowsCliCatalog } from "./windows-cli-catalog.js";
8
+ import { useRuntimeCatalog, instanceMachineId } from "./runtime-catalog.js";
9
+ import { NativeAgendToolsHint, tr } from "./agent-runtime.js";
10
+ register("nativeInstance", {
11
+ "zh-TW": { fixed:"Runtime 與 CLI 隨此對話保留。", workspace:"此 Agent 的對話會沿用這個工作區。" },
12
+ en: { fixed:"This conversation keeps its runtime and CLI.", workspace:"This agent's conversation keeps this workspace." },
13
+ });
14
+ const tn = key => t(`settings.${key}`);
15
+ export function NativeInstanceSettings({ name, inst, ctx, onClose }) {
16
+ const machineId = instanceMachineId(inst);
17
+ const catalog = useWindowsCliCatalog(!machineId), machines = useRuntimeCatalog(!!machineId);
18
+ const machine = machines.data?.machines?.find(item => item.id === machineId);
19
+ const native = (machineId ? machine?.inventory : catalog.data)?.clis?.find(cli => cli.id === inst.backend);
20
+ const [form,setForm] = useState(() => ({display_name:inst.display_name || name,description:inst.description || "",model:inst.model || "",systemPrompt:inst.systemPrompt || "",native_sandbox:inst.native_sandbox || "read-only",tags:Array.isArray(inst.tags) ? [...inst.tags] : []}));
21
+ const set = key => value => setForm(current => ({...current,[key]:value}));
22
+ const ready = !!form.display_name.trim() && !(form.native_sandbox === "read-only" && native?.readOnlySupport === false);
23
+ const stage = () => {
24
+ if (!ready) return;
25
+ const patch = {};
26
+ for (const [key,value] of Object.entries(form)) {
27
+ const before = key === "display_name" ? inst.display_name || name : key === "native_sandbox" ? inst.native_sandbox || "read-only" : key === "tags" ? inst.tags || [] : inst[key] || "";
28
+ const next = typeof value === "string" ? value.trim() : value;
29
+ if (JSON.stringify(next) !== JSON.stringify(before)) patch[key] = next;
30
+ }
31
+ if (Object.keys(patch).length) ctx.stageAgent(name,inst,patch);
32
+ onClose();
33
+ };
34
+ return html`<${Dialog} title=${ctx.shortName(name,inst)} onClose=${onClose} wide=${true} actions=${html`<button type="button" class="btn" onClick=${onClose}>${tn("cancel")}</button><button type="button" class="btn btn-primary" disabled=${!ready} onClick=${stage}>${tn("modalDone")}</button>`}>
35
+ <div class="form native-instance-settings"><p class="note"><${CliProductMark} id=${inst.backend} size=${20} /> Windows · ${cliLabel(inst.backend)} — ${t("nativeInstance.fixed")}</p>${machineId ? html`<p class="note runtime-instance-machine">${tr("machine")}: ${machine?.name || inst.machine_name || machineId}</p>` : null}
36
+ <label class="field"><span>${tn("displayName")}</span><input value=${form.display_name} maxLength=${64} onInput=${event => set("display_name")(event.target.value)} /></label>
37
+ <label class="field"><span>${tn("description")}</span><input value=${form.description} maxLength=${512} onInput=${event => set("description")(event.target.value)} /></label>
38
+ <div class="field"><label for="native-instance-model">${tn("model")}</label><${ModelSelect} id="native-instance-model" value=${form.model} onChange=${set("model")} backend=${inst.backend} instance=${name} runtime="windows" machineId=${machineId} inherit=${tr("model")} /></div>
39
+ <label class="field"><span>${tn("workingDir")}</span><input value=${inst.working_directory || ""} readOnly=${true} aria-readonly="true" /><small>${t("nativeInstance.workspace")}</small></label>
40
+ <label class="field"><span>${tr("access")}</span><select value=${form.native_sandbox} onChange=${event => set("native_sandbox")(event.target.value)}><option value="read-only" disabled=${native?.readOnlySupport === false}>${tr("read")}</option><option value="workspace-write">${tr("write")}</option></select></label>
41
+ <${NativeAgendToolsHint} cli=${native} sandbox=${form.native_sandbox} />
42
+ ${!ready && form.native_sandbox === "read-only" && native?.readOnlySupport === false ? html`<p class="note">${tr("noReadOnly")}</p>` : null}
43
+ <label class="field"><span>${tn("systemPrompt")}</span><textarea rows="5" maxLength=${8000} value=${form.systemPrompt} onInput=${event => set("systemPrompt")(event.target.value)} /></label>
44
+ <div class="field"><span>${tn("tags")}</span><${ChipList} items=${form.tags} onChange=${set("tags")} placeholder=${tn("tagPlaceholder")} label=${tn("tags")} /></div>
45
+ </div>
46
+ </${Dialog}>`;
47
+ }
@@ -0,0 +1,45 @@
1
+ /** Retained-pane recovery uses an explicit admin attestation; it never resubmits a chat message. */
2
+ import { html, useEffect, useRef, useState } from "/assets/app-html.js";
3
+ import { t } from "/assets/app-i18n.js";
4
+ import { guestPage } from "/assets/app-store.js";
5
+ import { Dialog } from "/assets/ui-dialog.js";
6
+ import { toast } from "/assets/ui-toast.js";
7
+ import "./chat-strings.js";
8
+ export function NativePaneRecovery({ name, inst, lease }) {
9
+ const [dialog, setDialog] = useState(false), [checked, setChecked] = useState(false), [busy, setBusy] = useState(false);
10
+ const confirmation = useRef(null), inFlight = useRef(false), recovery = inst.native_recovery;
11
+ const latest = useRef(null); latest.current = {name,recovery};
12
+ useEffect(() => { confirmation.current=null; setDialog(false); setChecked(false); }, [recovery?.launchId, recovery?.sessionId, recovery?.kind]);
13
+ const act = async (path, body) => {
14
+ if (inFlight.current || !lease.current()) return;
15
+ inFlight.current = true; setBusy(true);
16
+ try {
17
+ const response = await lease.fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, ...(body ? { body: JSON.stringify(body) } : {}) });
18
+ const result = await response.json();
19
+ if (!lease.current()) return;
20
+ if (!response.ok || result.error) toast(result.error || t("chat.disconnected"), false);
21
+ else { setDialog(false); setChecked(false); }
22
+ } catch (error) { if (lease.current()) toast(error.message || t("chat.disconnected"), false); }
23
+ finally { inFlight.current = false; if (lease.current()) setBusy(false); }
24
+ };
25
+ if (guestPage() || inst.native_adapter !== "pane") return null;
26
+ if (!recovery) return !inst.general_topic && ["stopped", "crashed"].includes(inst.status)
27
+ ? html`<div class="windows-agent-blocked"><button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/start/${encodeURIComponent(name)}`)}>${t("chat.start")}</button></div>` : null;
28
+ if (recovery.kind === "session-unconfirmed") return html`<div class="windows-agent-blocked" role="alert">
29
+ <p>${t(recovery.provider === "codex" ? "chat.paneCodexSessionUnconfirmed" : "chat.paneSessionUnconfirmed")}</p>
30
+ <button type="button" class="btn" disabled=${busy || !recovery.launchId} onClick=${() => { confirmation.current = {name,launchId:recovery.launchId,sessionId:recovery.sessionId??null}; setDialog(true); }}>${t("chat.paneNewConversation")}</button>
31
+ ${dialog ? html`<${Dialog} title=${t("chat.paneNewConversation")} onClose=${() => { if (!busy) setDialog(false); }}>
32
+ <p>${t("chat.paneNewConversationConfirm",inst.display_name || name)}</p>
33
+ <button type="button" class="btn" disabled=${busy || confirmation.current?.name !== name || confirmation.current?.launchId !== recovery.launchId || confirmation.current?.sessionId !== (recovery.sessionId??null)} onClick=${() => { const target=confirmation.current, now=latest.current; if(target?.name===now.name && target.launchId===now.recovery?.launchId && target.sessionId===(now.recovery?.sessionId??null)) act(`/ui/instances/${encodeURIComponent(name)}/new-conversation`,{confirmedNewConversation:true,launchId:target.launchId,sessionId:target.sessionId}); }}>${t("chat.paneNewConversation")}</button>
34
+ </${Dialog}>` : null}
35
+ </div>`;
36
+ return html`<div class="windows-agent-blocked" role="alert"><p>${t(recovery.kind === "host-upgrade" ? "chat.paneHostUpgrade" : "chat.paneRecoveryNeeded")}</p>
37
+ ${recovery.kind === "host-upgrade" ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/stop/${encodeURIComponent(name)}`)}>${t("chat.stopInstance")}</button>` : recovery.launchId ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => { confirmation.current = {name,launchId:recovery.launchId}; setChecked(false); setDialog(true); }}>${t("chat.paneRecover")}</button>` : null}
38
+ <button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/start/${encodeURIComponent(name)}`)}>${t("chat.start")}</button>
39
+ ${dialog ? html`<${Dialog} title=${t("chat.paneRecover")} onClose=${() => { if (!busy) setDialog(false); }}>
40
+ <p>${t("chat.paneRecoverConfirm", inst.display_name || name)}</p>
41
+ <label><input type="checkbox" checked=${checked} disabled=${busy} onChange=${event => setChecked(event.target.checked)} />${t("chat.paneProcessesStopped")}</label>
42
+ <button type="button" class="btn" disabled=${!checked || busy || confirmation.current?.name !== name || confirmation.current?.launchId !== recovery.launchId} onClick=${() => { if (checked && confirmation.current?.name === name && confirmation.current?.launchId === recovery.launchId) act(`/ui/instances/${encodeURIComponent(name)}/recover-pane`, { confirmedStopped: true, launchId: confirmation.current.launchId }); }}>${t("chat.paneRecoverConfirmed")}</button>
43
+ </${Dialog}>` : null}
44
+ </div>`;
45
+ }
@@ -1,3 +1,5 @@
1
+ import { NativePaneRecovery } from "./native-pane-recovery.js";
2
+ import { GeneralLocation } from "./general-location.js";
1
3
  // #1408 step 1: the chat panel. Behind the session gate (/ui/js/), loaded by the app with a dynamic import in "full"
2
4
  // mode only. boot() runs once per page (the chat store must hear the stream from the first frame); <ChatPanel> mounts
3
5
  // for /ui and /ui/chat/<name>.
@@ -11,9 +13,9 @@ import { lang, t } from "/assets/app-i18n.js";
11
13
  import { appStore, createStore, guestPage, guestTrusted, useStore } from "/assets/app-store.js";
12
14
  import { ComposerAuth } from "/assets/web-authorize.js";
13
15
  import { useLease } from "/assets/app-ctx.js";
14
- import { modelText, namesOf, namesStore, navStore as instanceNavStore } from "/assets/instance-nav.js";
16
+ import { modelText, namesOf, namesStore, navStore as instanceNavStore, supportsInstanceFeature } from "/assets/instance-nav.js";
15
17
  import { PanelHeader, onPanelKey, setTitle, statusClass, statusLabel, openDrawer, requestNewInstance } from "/assets/app-shell.js";
16
- import { detailsPath, textLinkSegments } from "/assets/app-route.js";
18
+ import { detailsPath, textLinkSegments, sectionPath } from "/assets/app-route.js";
17
19
  import { InstanceSwitch } from "/assets/instance-switch.js";
18
20
  import { HeaderTools } from "/assets/header-tools.js";
19
21
  import { navigate, navStore } from "/assets/app-nav.js";
@@ -29,7 +31,6 @@ import { applyTheme, clearAmbient, resolvedMode, themeFor, themeImageUrl, themeM
29
31
  import { colorMenuItem } from "/assets/identity-color.js";
30
32
  import { brokenPictures, cardFoldable, IdentityCard, useCard } from "/assets/identity-card.js";
31
33
  import { AgentWidget, instanceBinds, widgetFor } from "/assets/agent-widget.js";
32
-
33
34
  // #1629 r4 §1: the identity card under the chat's header — hidden by default; shown or not is this device's choice.
34
35
  // #2217 (the user, 2026-10-19): no row of its own; its picture and bio can be folded away (the banner stays) — this
35
36
  // device's choice too, kept the same way. The user (2026-10-20): no control on the card — one card button in the header
@@ -214,7 +215,7 @@ export function ChatPanel({ route, navKey }) {
214
215
  store.setCurrent(name && inst ? name : null);
215
216
  return () => store.setCurrent(null);
216
217
  }, [name, !!inst]);
217
- useEffect(() => { setTitle(name || t("app.chat")); }, [name, navKey]);
218
+ useEffect(() => { setTitle(inst?.runtime === "windows" ? inst.display_name || name : name || t("app.chat")); }, [name, navKey, inst?.runtime, inst?.display_name]);
218
219
  if (!name) {
219
220
  // #1854 (rank 7): one instruction — pick an agent, or with none at all, the one way to make one. Connecting a chat
220
221
  // app (the first-run card, #1519 P7) is offered in Settings, not here: it competed with the first agent.
@@ -259,6 +260,7 @@ function useResolvedMode(lease) {
259
260
  }
260
261
 
261
262
  function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
263
+ const attachmentsSupported = supportsInstanceFeature(inst,"attachments");
262
264
  const names = useStore(namesStore); // #1722: which name leads (the title)
263
265
  // #1629: this agent's theme, on this surface only; redrawn when the themes, the viewer's switch or the mode move.
264
266
  useStore(themesStore);
@@ -267,6 +269,17 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
267
269
  const themeKey = JSON.stringify(theme);
268
270
  const card = useCard(name, inst.card_v);
269
271
  const [dialog, setDialog] = useState(null); // "delete" | null
272
+ const [recovering,setRecovering] = useState(false);
273
+ const recoverNative = async () => {
274
+ if (recovering) return;
275
+ setRecovering(true);
276
+ try {
277
+ const response = await lease.fetch(`/ui/instances/${encodeURIComponent(name)}/recover`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({confirmedStopped:true})});
278
+ const result = await response.json();
279
+ if (lease.current() && (!response.ok || result.error)) toast(result.error || t("chat.disconnected"),false);
280
+ } catch(error) { if (lease.current()) toast(error.message || t("chat.disconnected"),false); }
281
+ finally { if (lease.current()) setRecovering(false); }
282
+ };
270
283
  const [wrap, setWrap] = useState(codeWrap);
271
284
  const wide = useStore(chatWide).on;
272
285
  const view = useRef(null), split = useRef(null), thread = useRef(null), bg = useRef(null), effectSurface = useRef(null);
@@ -289,12 +302,14 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
289
302
  const el = view.current; if (!el) return undefined;
290
303
  let depth = 0;
291
304
  const hasFiles = (e) => !!(e.dataTransfer && [...e.dataTransfer.types].includes("Files"));
292
- lease.on(el, "dragenter", (e) => { if (hasFiles(e)) { depth++; el.classList.add("dragging"); } });
293
- lease.on(el, "dragover", (e) => { if (hasFiles(e)) e.preventDefault(); });
294
- lease.on(el, "dragleave", (e) => { if (hasFiles(e) && --depth <= 0) { depth = 0; el.classList.remove("dragging"); } });
295
- lease.on(el, "drop", (e) => { depth = 0; el.classList.remove("dragging"); if (e.dataTransfer && e.dataTransfer.files.length) { e.preventDefault(); store.addFiles(name, e.dataTransfer.files); } });
296
- return undefined;
297
- }, [lease]);
305
+ const offs = [
306
+ lease.on(el, "dragenter", (e) => { if (attachmentsSupported && hasFiles(e)) { depth++; el.classList.add("dragging"); } }),
307
+ lease.on(el, "dragover", (e) => { if (hasFiles(e)) e.preventDefault(); }),
308
+ lease.on(el, "dragleave", (e) => { if (hasFiles(e) && --depth <= 0) { depth = 0; el.classList.remove("dragging"); } }),
309
+ lease.on(el, "drop", (e) => { depth = 0; el.classList.remove("dragging"); if (e.dataTransfer && e.dataTransfer.files.length) { e.preventDefault(); if (attachmentsSupported) store.addFiles(name, e.dataTransfer.files); else toast(t("chat.runtimeNoAttachments"),false); } }),
310
+ ];
311
+ return () => offs.forEach(off => off());
312
+ }, [lease,attachmentsSupported]);
298
313
  // Esc stops the agent's reply (as Esc in its terminal would): from the chat, while it works, and only when nothing
299
314
  // else that Esc would close is open (a dialog, a menu, the Session menu).
300
315
  useEffect(() => lease.hold(onPanelKey((e) => {
@@ -317,10 +332,11 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
317
332
  };
318
333
  const items = [
319
334
  // #1523 N2: Details is a page now (the Fleet side of this instance), not a dialog.
320
- { key: "details", label: t("chat.details"), icon: "info", onSelect: () => navigate(detailsPath(name)) },
321
- running ? null : { key: "start", label: t("chat.start"), icon: "play", onSelect: () => action("start") },
322
- running ? { key: "restart", label: t("chat.restart"), icon: "restart", onSelect: () => action("restart") } : null,
323
- running ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", onSelect: () => action("stop") } : null,
335
+ supportsInstanceFeature(inst,"details") ? { key: "details", label: t("chat.details"), icon: "info", onSelect: () => navigate(detailsPath(name)) } : null,
336
+ inst.runtime === "windows" ? {key:"settings",label:t("app.settings"),icon:"settings",onSelect:() => navigate(sectionPath("agents"))} : null,
337
+ running || !supportsInstanceFeature(inst,"lifecycle") ? null : { key: "start", label: t("chat.start"), icon: "play", onSelect: () => action("start") },
338
+ running && supportsInstanceFeature(inst,"lifecycle") ? { key: "restart", label: t("chat.restart"), icon: "restart", onSelect: () => action("restart") } : null,
339
+ running && supportsInstanceFeature(inst,"lifecycle") ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", onSelect: () => action("stop") } : null,
324
340
  // #1958: its identity colour and where it shows (the public link too, as profile edits).
325
341
  colorMenuItem(name, inst),
326
342
  // #1629: a person's control over this agent's theme — not over the public link (it only views themes).
@@ -337,10 +353,12 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
337
353
  // #1269 quick actions: the model and effort it runs, each a way to /model or /effort (the same pick list).
338
354
  const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
339
355
  const sub = html`<span class="status"><span class=${`dot ${cls}`} aria-hidden="true"></span>${statusLabel(inst, exec, awaiting)}</span>
340
- ${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => pick("model")}>${modelText(inst)}</button>` : null}
341
- ${inst.effort ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
356
+ ${inst.runtime === "windows" ? html`<span class="tag">Windows</span>` : null}
357
+ ${inst.machine_id ? html`<span class="tag runtime-machine-tag" title=${t("chat.machineName",inst.machine_name || inst.machine_id)}>${inst.machine_name || inst.machine_id}</span>${inst.machine_status !== "online" ? html`<span class="tag runtime-machine-offline">${t(inst.machine_status === "revoked" ? "chat.machineRevoked" : "chat.machineOffline")}</span>` : null}` : null}
358
+ ${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => supportsInstanceFeature(inst,"commands") ? pick("model") : navigate(sectionPath("agents"))}>${modelText(inst)}</button>` : null}
359
+ ${inst.effort && supportsInstanceFeature(inst,"effort") ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
342
360
  return html`<div class=${`panel p-chat${wrap ? " wrap-code" : ""}`} data-width=${wide ? "full" : undefined} ref=${view}>
343
- <${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? (guestTrusted() ? html`<${InstanceSwitch} name=${name} current="chat" guest=${true} />` : null) : html`<${InstanceSwitch} name=${name} current="chat" />`}><${CardMenu} card=${card} /><${HeaderTools} navKey=${navKey} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
361
+ <${PanelHeader} title=${inst.runtime === "windows" ? inst.display_name || name : headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? (guestTrusted() ? html`<${InstanceSwitch} name=${name} instance=${inst} current="chat" guest=${true} />` : null) : html`<${InstanceSwitch} name=${name} instance=${inst} current="chat" />`}><${CardMenu} card=${card} /><${HeaderTools} navKey=${navKey} usage=${supportsInstanceFeature(inst,"usage")} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
344
362
  <${ChatCard} key=${name} name=${name} card=${card} />
345
363
  <${AgentWidget} slot="chat_header" widget=${widgetFor(name, "chat_header")} binds=${instanceBinds(inst, exec, awaiting)} />
346
364
  <div class="chat-split" ref=${split}>
@@ -348,7 +366,10 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
348
366
  ${theme && theme.background ? html`<img ref=${bg} class=${`th-bg th-bg-${theme.background.fit}`} src=${themeImageUrl(name, theme.background.image)} alt="" aria-hidden="true" decoding="async" />` : null}
349
367
  <${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
350
368
  <${AmbientMotionNote} name=${name} />
351
- <${Thread} name=${name} th=${thread} />
369
+ ${inst.general_topic ? html`<${GeneralLocation} name=${name} attentionOnly=${true} />` : null}
370
+ <${NativePaneRecovery} name=${name} inst=${inst} lease=${lease} />
371
+ ${inst.native_blocked ? html`<div class="windows-agent-blocked" role="alert"><p>${t("chat.nativeBlocked")}</p><button type="button" class="btn" disabled=${recovering} onClick=${recoverNative}>${t("chat.nativeRecover")}</button></div>` : null}
372
+ <${Thread} name=${name} label=${inst.runtime === "windows" ? inst.display_name || name : name} th=${thread} />
352
373
  <${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${theme ? theme.workingLabel : undefined} />
353
374
  <${ChatAmbient} name=${name} surface=${effectSurface} part="front" />
354
375
  </div>
@@ -356,8 +377,8 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
356
377
  keyOf=${msgKey} reveal=${(k) => thread.current && thread.current.reveal(k)} download=${downloadHtml} allow=${(commit) => setPreviewOptIn(true, commit)} />
357
378
  </div>
358
379
  <${LightboxHost} />
359
- <div data-trust="drop" class="drop-overlay" aria-hidden="true"><div class="drop-card"><${Icon} name="attach" size=${32} /><span>${t("chat.dropHere")}</span></div></div>
360
- ${dialog === "delete" ? html`<${DeleteDialog} name=${name} onClose=${() => setDialog(null)} />` : null}
380
+ ${attachmentsSupported ? html`<div data-trust="drop" class="drop-overlay" aria-hidden="true"><div class="drop-card"><${Icon} name="attach" size=${32} /><span>${t("chat.dropHere")}</span></div></div>` : null}
381
+ ${dialog === "delete" ? html`<${DeleteDialog} name=${name} inst=${inst} onClose=${() => setDialog(null)} />` : null}
361
382
  </div>`;
362
383
  }
363
384
 
@@ -467,10 +488,11 @@ function follow(name, thread) {
467
488
  }
468
489
 
469
490
  /** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
470
- function Thread({ name, th }) {
491
+ function Thread({ name, label = name, th }) {
471
492
  const scroller = useRef(null), host = useRef(null);
472
493
  const [jump, setJump] = useState({ show: false, unseen: 0 });
473
494
  const [empty, setEmpty] = useState(false);
495
+ useLayoutEffect(() => { keptThreads.get(name)?.list.setAttribute("aria-label",t("chat.conversationWith",label)); },[name,label]);
474
496
  useLayoutEffect(() => {
475
497
  const doc = host.current.ownerDocument;
476
498
  let e = keptThreads.get(name);
@@ -484,6 +506,7 @@ function Thread({ name, th }) {
484
506
  if (kept) trimKept(name);
485
507
  const shownAt = now();
486
508
  const thread = e.thread, sc = scroller.current, ls = e.list;
509
+ ls.setAttribute("aria-label",t("chat.conversationWith",label));
487
510
  e.sink = { setJump, setEmpty };
488
511
  host.current.append(ls);
489
512
  thread.attach(sc);
@@ -544,9 +567,9 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
544
567
  <${CommandCard} name=${name} lease=${lease} />
545
568
  <${QuickActions} name=${name} inst=${inst} lease=${lease} />
546
569
  ${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
547
- <${Composer} name=${name} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
570
+ <${Composer} name=${name} inst=${inst} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
548
571
  <div class="composer-row">${guestPage() ? html`<span class="guest-note" title=${t("chat.guestNote")}>${t("chat.guestNote")}</span>` : html`<${ComposerAuth} />`}<span
549
- class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t(touchScreen() ? "chat.composerHintTouch" : "chat.composerHint")}</span></div>
572
+ class="hint" title=${t(guestPage() || !supportsInstanceFeature(inst,"commands") ? "chat.guestMore" : "chat.composerMore")}>${t(supportsInstanceFeature(inst,"commands") ? touchScreen() ? "chat.composerHintTouch" : "chat.composerHint" : touchScreen() ? "chat.nativeComposerHintTouch" : "chat.nativeComposerHint")}</span></div>
550
573
  </div></div>`;
551
574
  }
552
575
 
@@ -641,7 +664,7 @@ function replySnippet(r) {
641
664
  return text || (r.media === "photo" ? t("chat.replyPhoto") : r.media === "document" ? t("chat.replyFile") : "");
642
665
  }
643
666
 
644
- function Composer({ name, label = name, lease, busy }) {
667
+ function Composer({ name, inst, label = name, lease, busy }) {
645
668
  const s = store.state;
646
669
  const input = useRef(null), fileIn = useRef(null);
647
670
  const [, tick] = useState(0);
@@ -662,7 +685,10 @@ function Composer({ name, label = name, lease, busy }) {
662
685
  const guest = guestPage();
663
686
  const app = useStore(appStore);
664
687
  const guestAllowed = guest ? (app.instances.find((i) => i.name === name)?.commands ?? []) : null;
665
- const palette = files.length || closedFor === value || (guest && !guestAllowed.length) ? null : paletteFor(value, guestAllowed || undefined);
688
+ const attachmentsSupported = supportsInstanceFeature(inst,"attachments"), commandsSupported = supportsInstanceFeature(inst,"commands");
689
+ const machineUnavailable = !!inst?.machine_id && inst.machine_status !== "online";
690
+ const sendingBlocked = machineUnavailable || inst?.native_blocked || inst?.native_busy || (busy && !supportsInstanceFeature(inst,"send_while_busy"));
691
+ const palette = !commandsSupported || files.length || closedFor === value || (guest && !guestAllowed.length) ? null : paletteFor(value, guestAllowed || undefined);
666
692
  const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
667
693
  // Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300). Empty, it is one line
668
694
  // (#2478 — a long name's placeholder made the box two lines tall).
@@ -684,6 +710,9 @@ function Composer({ name, label = name, lease, busy }) {
684
710
  // #2342: a completion is a step Ctrl+Z goes back from (to what was typed); a command that runs consumes the line.
685
711
  const complete = (c) => { recordEdit(input.current); setValue(`/${c.name}${c.arg ? " " : ""}`); };
686
712
  const submit = () => {
713
+ if (sendingBlocked) return;
714
+ if (!attachmentsSupported && files.length) { toast(t("chat.runtimeNoAttachments"),false); return; }
715
+ if (!commandsSupported && parseCommandLine(value)) { toast(t("chat.runtimeNoCommands"),false); return; }
687
716
  if (pick) {
688
717
  if (value === `/${pick.name}` && !needsArgument(pick.name)) runLine({ command: pick.name, args: "" });
689
718
  else complete(pick);
@@ -719,11 +748,13 @@ function Composer({ name, label = name, lease, busy }) {
719
748
  const onPaste = (e) => {
720
749
  const list = e.clipboardData && e.clipboardData.files;
721
750
  const text = e.clipboardData ? e.clipboardData.getData("text") : "";
722
- if (list && list.length) { store.addFiles(name, list); if (!text) e.preventDefault(); return; }
723
- if (R().isLongPaste(text) && store.attachPastedText(name, text)) e.preventDefault();
751
+ if (list && list.length) { if (!attachmentsSupported) { if (!text) e.preventDefault(); toast(t("chat.runtimeNoAttachments"),false); return; } store.addFiles(name, list); if (!text) e.preventDefault(); return; }
752
+ if (attachmentsSupported && R().isLongPaste(text) && store.attachPastedText(name, text)) e.preventDefault();
724
753
  };
725
754
  const optId = (c) => `cmd-${c.name}`;
726
755
  return html`<div class="composer-box">
756
+ ${machineUnavailable ? html`<p class="note runtime-machine-unavailable" role="status">${t(inst.machine_status === "revoked" ? "chat.machineRevokedHint" : "chat.machineUnavailable",inst.machine_name || inst.machine_id)}</p>` : null}
757
+ ${inst?.native_busy && !busy && !inst?.native_blocked ? html`<p class="note" role="status">${t("chat.nativeBusy")}</p>` : null}
727
758
  ${palette ? html`<ul data-trust="palette" class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
728
759
  ${palette.length ? palette.map((c) => html`<li key=${c.name} id=${optId(c)} role="option" aria-selected=${c === pick ? "true" : "false"}
729
760
  class=${c === pick ? "on" : ""} onMouseDown=${(e) => { e.preventDefault(); complete(c); }}>
@@ -737,9 +768,9 @@ function Composer({ name, label = name, lease, busy }) {
737
768
  </div>` : null}
738
769
  ${files.length ? html`<div class="pending-files">${files.map((f, i) => html`<${FileChip} key=${`${f.name}-${i}`} f=${f} i=${i} name=${name} />`)}</div>` : null}
739
770
  <div class="composer">
740
- <button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
771
+ ${attachmentsSupported ? html`<button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
741
772
  <input ref=${fileIn} type="file" multiple hidden
742
- onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />
773
+ onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />` : null}
743
774
  ${/* #2478: the placeholder is drawn beside the box, one line ending in "…" — a textarea's own placeholder is clipped,
744
775
  never ellipsized, and a long name wrapped it to two lines. The box keeps the words as its accessible name. Its own
745
776
  placeholder is one space: :placeholder-shown hides the drawn one (app.css) the moment the box holds text, an
@@ -751,7 +782,7 @@ function Composer({ name, label = name, lease, busy }) {
751
782
  placeholder=" "></textarea><span class="composer-ph" aria-hidden="true">${t("chat.composerPlaceholder", label)}</span></span>
752
783
  <button id="stopBtn" type="button" class="btn btn-stop" hidden=${!busy} title=${t("chat.stopReplyTitle")}
753
784
  disabled=${!!s.cancelling[name]} onClick=${() => store.cancelReply(name)}><${Icon} name="stop" size=${14} /><span class="stop-label">${t("chat.stopReply")}</span></button>
754
- <button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content}
785
+ <button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content || sendingBlocked}
755
786
  onMouseDown=${(e) => e.preventDefault()}
756
787
  aria-label=${t("chat.send")} title=${t("chat.send")} onClick=${submit}><${Icon} name="send" size=${16} /><span class="send-label">${t("chat.send")}</span></button>
757
788
  </div></div>`;
@@ -775,7 +806,7 @@ function FileChip({ f, i, name }) {
775
806
 
776
807
  // ── Dialogs ──
777
808
 
778
- function DeleteDialog({ name, onClose }) {
809
+ function DeleteDialog({ name, inst, onClose }) {
779
810
  // The dialog's own lease: it ends when the dialog goes (closed, or the chat left for another page). The delete
780
811
  // may still finish on the server, and says so, but a dialog that is gone never closes or navigates the page the
781
812
  // person is on now (#1425 review).
@@ -791,7 +822,7 @@ function DeleteDialog({ name, onClose }) {
791
822
  // follows the request. From then on the dialog is done: the decision is said in a toast, and it never closes or
792
823
  // navigates anything (the person may be anywhere by then).
793
824
  try {
794
- const res = await confirmedWrite(`/ui/instances/${encodeURIComponent(name)}/delete`, { method: "POST", body: { confirm: typed }, label: t("chat.deleteTitle", name),
825
+ const res = await confirmedWrite(inst?.runtime === "windows" ? `/ui/instances/${encodeURIComponent(name)}` : `/ui/instances/${encodeURIComponent(name)}/delete`, { method: inst?.runtime === "windows" ? "DELETE" : "POST", body: inst?.runtime === "windows" ? {} : { confirm: typed }, label: t("chat.deleteTitle", name),
795
826
  onPending: (_id, view) => { handedOver = true; toast(t(selfConfirmed(view) ? "app.pendingSelfSent" : "app.pendingSent")); if (lease.current()) { setBusy(false); onClose(); } } });
796
827
  r = res.ok ? res.body || {} : { error: (res.body && res.body.error) || `HTTP ${res.status}` };
797
828
  } catch (err) { r = { error: err && err.message ? err.message : t("chat.disconnected") }; }
@@ -1,3 +1,4 @@
1
+ import { NativePaneRecovery } from "./native-pane-recovery.js";
1
2
  // #1523 N2 (§3.3, Q1 = B): Details — "the Fleet side of one instance", at /ui/fleet/agent/<name>. Behind the session
2
3
  // gate (/ui/js/). What it holds:
3
4
  // - runtime and recent activity (what Chat's details dialog showed, now a page);
@@ -158,7 +159,7 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
158
159
  }
159
160
  return html`<div data-effect-panel="details" class="panel p-details">
160
161
  <${PanelHeader} title=${name} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="details" />`}><${HeaderTools} navKey=${navKey} /><${Menu} items=${items} label=${t("app.more")} /></${PanelHeader}>
161
- <div data-trust="panel-content" class="panel-body"><div class="col" ref=${body}>${busy ? html`<p class="note" role="status">${t("chat.dWorking")}</p>` : null}<${IdentityCard} name=${name} card=${card} />
162
+ <div data-trust="panel-content" class="panel-body"><div class="col" ref=${body}><${NativePaneRecovery} name=${name} inst=${inst} lease=${lease} />${busy ? html`<p class="note" role="status">${t("chat.dWorking")}</p>` : null}<${IdentityCard} name=${name} card=${card} />
162
163
  <${AgentWidget} slot="details" widget=${widgetFor(name, "details")} binds=${instanceBinds(inst, exec, awaiting)} />${content}</div></div>
163
164
  </div>`;
164
165
  }