oysterun 1.6.0-beta.21 → 1.6.0-beta.22

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 (241) hide show
  1. package/README.md +2 -7
  2. package/bin/oysterun.mjs +25 -22
  3. package/dev/client/connectivity/host-connection-feedback.mjs +3 -1
  4. package/dev/client/connectivity/logical-host-connection.mjs +4 -0
  5. package/dev/client/connectivity/managed-host-browser.mjs +36 -6
  6. package/dev/client/web/index.html +821 -869
  7. package/dev/client/web-chat/dist/assets/{ReactPrism-C6YiTZTC.js → ReactPrism-J2mp0sGV.js} +1 -1
  8. package/dev/client/web-chat/dist/assets/{ReactPrism-C6YiTZTC.js.map → ReactPrism-J2mp0sGV.js.map} +1 -1
  9. package/dev/client/web-chat/dist/assets/SessionsSetupApp-CkENGhE-.css +1 -0
  10. package/dev/client/web-chat/dist/assets/SessionsSetupApp-DMSLgqp_.js +6348 -0
  11. package/dev/client/web-chat/dist/assets/{SessionsSetupApp-CKJOSPFP.js.map → SessionsSetupApp-DMSLgqp_.js.map} +1 -1
  12. package/dev/client/web-chat/dist/assets/{index-CN_FoTB1.js → index-BfrZKoYM.js} +2 -2
  13. package/dev/client/web-chat/dist/assets/{index-CN_FoTB1.js.map → index-BfrZKoYM.js.map} +1 -1
  14. package/dev/client/web-chat/dist/assets/{index-DCQFwgGf.js → index-C0U6oqMv.js} +1 -1
  15. package/dev/client/web-chat/dist/assets/{index-DCQFwgGf.js.map → index-C0U6oqMv.js.map} +1 -1
  16. package/dev/client/web-chat/dist/assets/{index-Ae5I4LHp.js → index-CG0A3OzI.js} +2 -2
  17. package/dev/client/web-chat/dist/assets/{index-Ae5I4LHp.js.map → index-CG0A3OzI.js.map} +1 -1
  18. package/dev/client/web-chat/dist/assets/{matrix-entry-abAy3aOo.js → matrix-entry-4XhjfS2j.js} +165 -165
  19. package/dev/client/web-chat/dist/assets/matrix-entry-4XhjfS2j.js.map +1 -0
  20. package/dev/client/web-chat/dist/assets/{pdf-DN7oVvM6.js → pdf-DjW36tod.js} +3 -3
  21. package/dev/client/web-chat/dist/assets/{pdf-DN7oVvM6.js.map → pdf-DjW36tod.js.map} +1 -1
  22. package/dev/client/web-chat/dist/assets/{web-Dabw6cJi.js → web-B-85Q5Fj.js} +1 -1
  23. package/dev/client/web-chat/dist/assets/{web-Dabw6cJi.js.map → web-B-85Q5Fj.js.map} +1 -1
  24. package/dev/client/web-chat/dist/assets/{web-BlYuDlOV.js → web-T-l_F_RW.js} +1 -1
  25. package/dev/client/web-chat/dist/assets/{web-BlYuDlOV.js.map → web-T-l_F_RW.js.map} +1 -1
  26. package/dev/client/web-chat/dist/index.html +1 -1
  27. package/docs/routec/config_hierachy.md +28 -22
  28. package/docs/routec/dev/host_update_runtime_identity.md +42 -1
  29. package/docs/routec/dev/npmjs_publish.md +46 -16
  30. package/docs/routec/form_UI_fields.md +1 -2
  31. package/docs/routec/host_cloud_app_auth.md +4 -4
  32. package/docs/routec/host_setup.md +28 -37
  33. package/docs/routec/oysterun_operation_interface_spec.md +6 -5
  34. package/docs/routec/skill_installation.md +0 -1
  35. package/host-service/README.md +18 -25
  36. package/host-service/adapters/claude-code-adapter.mjs +123 -19
  37. package/host-service/agent-config-migration.mjs +3 -2
  38. package/host-service/agent-config.mjs +26 -4
  39. package/host-service/apns-complete-message-dispatcher.mjs +6 -7
  40. package/host-service/apns-config.mjs +2 -1
  41. package/host-service/apns-device-store.mjs +7 -3
  42. package/host-service/assets/product-skills/Oysterun/SKILL.md +0 -1
  43. package/host-service/assets/product-skills/Oysterun/modules/oysterun-session-chat/SKILL.md +11 -10
  44. package/host-service/assets/product-skills/Oysterun/modules/oysterun-sessions/SKILL.md +24 -10
  45. package/host-service/assets/product-skills/Oysterun/modules/oysterun-website/SKILL.md +2 -2
  46. package/host-service/assets/product-skills/oysterun-session-chat/SKILL.md +11 -10
  47. package/host-service/assets/product-skills/oysterun-sessions/SKILL.md +24 -10
  48. package/host-service/assets/product-skills/oysterun-website/SKILL.md +2 -2
  49. package/host-service/atomic-file.mjs +184 -14
  50. package/host-service/claude-acp-model-discovery.mjs +106 -22
  51. package/host-service/claude-acp-model-error-classification.mjs +97 -0
  52. package/host-service/cli/prepare-storage-layout.mjs +41 -0
  53. package/host-service/cli/product-cli.mjs +136 -46
  54. package/host-service/cli/recover-installer-lifecycle.mjs +2 -1
  55. package/host-service/cli-profile-storage.mjs +69 -0
  56. package/host-service/cloud-identity-envelope.mjs +93 -0
  57. package/host-service/complete-cli-uninstall.mjs +8 -3
  58. package/host-service/config-storage-shape.mjs +48 -0
  59. package/host-service/config.mjs +167 -179
  60. package/host-service/connectivity/managed-tunnel-runtime-status.mjs +5 -5
  61. package/host-service/connectivity/transport-usage-circuit-store.mjs +2 -6
  62. package/host-service/desired-open-session-state.mjs +5 -3
  63. package/host-service/frp-agent.mjs +14 -10
  64. package/host-service/host-lifecycle-recovery.mjs +29 -15
  65. package/host-service/host-login-bootstrap-tokens.mjs +21 -34
  66. package/host-service/host-restart-login-gate.mjs +4 -7
  67. package/host-service/host-runtime-diagnostics.mjs +10 -17
  68. package/host-service/host-telemetry.mjs +4 -5
  69. package/host-service/initial-preflight-recovery.mjs +2 -1
  70. package/host-service/installer-recovery-guidance.mjs +4 -3
  71. package/host-service/installer-reinstall-bindings.mjs +2 -1
  72. package/host-service/installer-reinstall-target.mjs +2 -1
  73. package/host-service/large-tool-call-store.mjs +4 -2
  74. package/host-service/lifecycle-compat-preload.mjs +1 -1
  75. package/host-service/lifecycle-compatibility.mjs +5 -4
  76. package/host-service/lifecycle-continuity-assets.mjs +24 -10
  77. package/host-service/lifecycle-continuity-probe.mjs +9 -3
  78. package/host-service/lifecycle-continuity-projection.mjs +2 -0
  79. package/host-service/lifecycle-continuity-runner.mjs +5 -12
  80. package/host-service/lifecycle-continuity.mjs +99 -46
  81. package/host-service/lifecycle-diagnostics.mjs +54 -8
  82. package/host-service/lifecycle-service-render-bundle.mjs +3 -2
  83. package/host-service/lifecycle-user-path.mjs +4 -0
  84. package/host-service/log-retention.mjs +199 -0
  85. package/host-service/mail-notification-dispatcher.mjs +6 -7
  86. package/host-service/mail-store.mjs +2 -1
  87. package/host-service/managed-identity-lifecycle.mjs +3 -31
  88. package/host-service/matrix-room-binding.mjs +8 -7
  89. package/host-service/node-npm-runtime-identity.mjs +2 -1
  90. package/host-service/onboard.mjs +9 -17
  91. package/host-service/package.json +1 -1
  92. package/host-service/product-runtime-host-origin.mjs +10 -13
  93. package/host-service/provider-model-params.mjs +34 -13
  94. package/host-service/provider-model-refresh-runner.mjs +16 -3
  95. package/host-service/registered-update-authority.mjs +0 -13
  96. package/host-service/replay-claude-acp-raw-events.mjs +6 -3
  97. package/host-service/restart-restore-state.mjs +4 -7
  98. package/host-service/routec-artifacts.mjs +6 -4
  99. package/host-service/routec-matrix-media-store.mjs +8 -12
  100. package/host-service/routec-matrix-sqlite-store.mjs +43 -71
  101. package/host-service/routec-matrix-storage-adapter.mjs +5 -2
  102. package/host-service/routec-matrix-storage-paths.mjs +16 -2
  103. package/host-service/routec-matrix-stream-seq-repair.mjs +5 -6
  104. package/host-service/routec-viewport-geometry-diagnostics.mjs +2 -1
  105. package/host-service/scheduler-setup-snapshot-contract.mjs +12 -0
  106. package/host-service/scheduler-store.mjs +2 -1
  107. package/host-service/server.mjs +276 -243
  108. package/host-service/service-control-token.mjs +7 -13
  109. package/host-service/service-definition-migration.mjs +70 -13
  110. package/host-service/session-assets.mjs +3 -2
  111. package/host-service/session-collection-service.mjs +13 -0
  112. package/host-service/session-history.mjs +15 -34
  113. package/host-service/session-loop-definitions.mjs +4 -21
  114. package/host-service/session-manager.mjs +156 -57
  115. package/host-service/session-name-counter-store.mjs +2 -1
  116. package/host-service/session-notification-settings-store.mjs +3 -2
  117. package/host-service/session-tags.mjs +11 -34
  118. package/host-service/session-transcript-target.mjs +4 -7
  119. package/host-service/setup-input-file.mjs +6 -4
  120. package/host-service/setup-provider-model-defaults.mjs +6 -1
  121. package/host-service/setup.mjs +58 -576
  122. package/host-service/stopped-user-host-package.mjs +2 -1
  123. package/host-service/storage-continuity.mjs +112 -0
  124. package/host-service/storage-database.mjs +115 -0
  125. package/host-service/storage-layout-cli.mjs +15 -0
  126. package/host-service/storage-layout.mjs +377 -0
  127. package/host-service/storage-lifecycle.mjs +306 -0
  128. package/host-service/storage-lock.mjs +178 -0
  129. package/host-service/storage-migration.mjs +678 -0
  130. package/host-service/storage-references.mjs +72 -0
  131. package/host-service/storage-service-binding.mjs +255 -0
  132. package/host-service/storage-snapshot.mjs +101 -0
  133. package/host-service/telegram-adapter.mjs +7 -1
  134. package/host-service/telegram-bridge.mjs +195 -0
  135. package/host-service/telegram-media.mjs +11 -14
  136. package/host-service/test-fixtures/p316/rca-run1-chunks.json +71 -0
  137. package/host-service/test-fixtures/p316/rca-run2-chunks.json +213 -0
  138. package/host-service/tool-event-detail-store.mjs +8 -11
  139. package/host-service/transcript-store.mjs +16 -14
  140. package/host-service/update-reminder-state.mjs +7 -8
  141. package/host-service/upload-manager.mjs +3 -2
  142. package/host-service/voice-input-service.mjs +5 -13
  143. package/host-service/voice-input-usage-store.mjs +2 -1
  144. package/package.json +2 -1
  145. package/static/site/docs/choose-and-recover-a-provider/assets/publication/provider-refresh-ready.png +0 -0
  146. package/static/site/docs/choose-and-recover-a-provider/assets/publication-screenshots.json +22 -12
  147. package/static/site/docs/choose-and-recover-a-provider/en/index.html +2 -2
  148. package/static/site/docs/choose-and-recover-a-provider/en/index.md +2 -2
  149. package/static/site/docs/choose-and-recover-a-provider/zh/index.html +2 -2
  150. package/static/site/docs/choose-and-recover-a-provider/zh/index.md +2 -2
  151. package/static/site/docs/configure-session-files-and-settings/assets/publication/folder-picker-focused.png +0 -0
  152. package/static/site/docs/configure-session-files-and-settings/assets/publication/session-profile-root.png +0 -0
  153. package/static/site/docs/configure-session-files-and-settings/assets/publication/session-setup-confirmed.png +0 -0
  154. package/static/site/docs/configure-session-files-and-settings/assets/publication-screenshots.json +18 -18
  155. package/static/site/docs/configure-session-files-and-settings/en/index.html +4 -4
  156. package/static/site/docs/configure-session-files-and-settings/en/index.md +3 -3
  157. package/static/site/docs/configure-session-files-and-settings/zh/index.html +4 -4
  158. package/static/site/docs/configure-session-files-and-settings/zh/index.md +3 -3
  159. package/static/site/docs/generated_manifest.json +82 -82
  160. package/static/site/docs/host-authentication-and-connection/assets/publication/sessions-connected-managed.png +0 -0
  161. package/static/site/docs/host-authentication-and-connection/assets/publication-screenshots.json +13 -13
  162. package/static/site/docs/host-authentication-and-connection/en/index.html +7 -7
  163. package/static/site/docs/host-authentication-and-connection/en/index.md +9 -9
  164. package/static/site/docs/host-authentication-and-connection/zh/index.html +7 -7
  165. package/static/site/docs/host-authentication-and-connection/zh/index.md +9 -9
  166. package/static/site/docs/install-and-use-a-skill/en/index.html +0 -1
  167. package/static/site/docs/install-and-use-a-skill/en/index.md +0 -4
  168. package/static/site/docs/install-and-use-a-skill/zh/index.html +0 -1
  169. package/static/site/docs/install-and-use-a-skill/zh/index.md +0 -4
  170. package/static/site/docs/restore-work-after-a-host-restart/assets/publication/restart-control.png +0 -0
  171. package/static/site/docs/restore-work-after-a-host-restart/assets/publication-screenshots.json +7 -7
  172. package/static/site/docs/restore-work-after-a-host-restart/en/index.html +2 -2
  173. package/static/site/docs/restore-work-after-a-host-restart/en/index.md +2 -2
  174. package/static/site/docs/restore-work-after-a-host-restart/zh/index.html +2 -2
  175. package/static/site/docs/restore-work-after-a-host-restart/zh/index.md +2 -2
  176. package/static/site/docs/set-up-and-understand-a-host/assets/publication/host-identity-browse-root.png +0 -0
  177. package/static/site/docs/set-up-and-understand-a-host/assets/publication/host-setup-managed-ready.png +0 -0
  178. package/static/site/docs/set-up-and-understand-a-host/assets/publication/host-setup-seven-steps.png +0 -0
  179. package/static/site/docs/set-up-and-understand-a-host/assets/publication/session-profile-identity.png +0 -0
  180. package/static/site/docs/set-up-and-understand-a-host/assets/publication/session-setup-identity.png +0 -0
  181. package/static/site/docs/set-up-and-understand-a-host/assets/publication/website-link.png +0 -0
  182. package/static/site/docs/set-up-and-understand-a-host/assets/publication-screenshots.json +63 -51
  183. package/static/site/docs/set-up-and-understand-a-host/en/index.html +17 -17
  184. package/static/site/docs/set-up-and-understand-a-host/en/index.md +16 -48
  185. package/static/site/docs/set-up-and-understand-a-host/zh/index.html +17 -17
  186. package/static/site/docs/set-up-and-understand-a-host/zh/index.md +16 -48
  187. package/static/site/install.sh +83 -21
  188. package/static/site_source/content/AGENTS.md +5 -12
  189. package/static/site_source/content/tutorials/published/choose-and-recover-a-provider/assets/publication/provider-refresh-ready.png +0 -0
  190. package/static/site_source/content/tutorials/published/choose-and-recover-a-provider/assets/publication-screenshots.json +22 -12
  191. package/static/site_source/content/tutorials/published/choose-and-recover-a-provider/en/article.html +2 -2
  192. package/static/site_source/content/tutorials/published/choose-and-recover-a-provider/zh/article.html +2 -2
  193. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/assets/publication/folder-picker-focused.png +0 -0
  194. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/assets/publication/session-profile-root.png +0 -0
  195. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/assets/publication/session-setup-confirmed.png +0 -0
  196. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/assets/publication-screenshots.json +18 -18
  197. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/en/article.html +3 -3
  198. package/static/site_source/content/tutorials/published/configure-session-files-and-settings/zh/article.html +3 -3
  199. package/static/site_source/content/tutorials/published/host-authentication-and-connection/assets/publication/sessions-connected-managed.png +0 -0
  200. package/static/site_source/content/tutorials/published/host-authentication-and-connection/assets/publication-screenshots.json +13 -13
  201. package/static/site_source/content/tutorials/published/host-authentication-and-connection/en/article.html +6 -6
  202. package/static/site_source/content/tutorials/published/host-authentication-and-connection/zh/article.html +6 -6
  203. package/static/site_source/content/tutorials/published/install-and-use-a-skill/en/article.html +0 -1
  204. package/static/site_source/content/tutorials/published/install-and-use-a-skill/zh/article.html +0 -1
  205. package/static/site_source/content/tutorials/published/restore-work-after-a-host-restart/assets/publication/restart-control.png +0 -0
  206. package/static/site_source/content/tutorials/published/restore-work-after-a-host-restart/assets/publication-screenshots.json +7 -7
  207. package/static/site_source/content/tutorials/published/restore-work-after-a-host-restart/en/article.html +2 -2
  208. package/static/site_source/content/tutorials/published/restore-work-after-a-host-restart/zh/article.html +2 -2
  209. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/host-identity-browse-root.png +0 -0
  210. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/host-setup-managed-ready.png +0 -0
  211. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/host-setup-seven-steps.png +0 -0
  212. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/session-profile-identity.png +0 -0
  213. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/session-setup-identity.png +0 -0
  214. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication/website-link.png +0 -0
  215. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/assets/publication-screenshots.json +63 -51
  216. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/en/article.html +16 -16
  217. package/static/site_source/content/tutorials/published/set-up-and-understand-a-host/zh/article.html +16 -16
  218. package/static/site_source/downloads/install.sh +83 -21
  219. package/tool_scripts/install_oysterun_update.mjs +18 -33
  220. package/tool_scripts/oysterun_continuity_transaction.mjs +97 -93
  221. package/tool_scripts/oysterun_npm_package_manifest.mjs +15 -1
  222. package/tool_scripts/oysterun_service_common.sh +72 -15
  223. package/tool_scripts/oysterun_setup_input.sh +77 -16
  224. package/tool_scripts/oysterun_update_restart_runner.mjs +3 -5
  225. package/tool_scripts/p233_legacy_bootstrap.mjs +1 -1
  226. package/tool_scripts/run_oysterun_host.sh +1 -0
  227. package/tool_scripts/stack_dashboard_credentials.mjs +28 -12
  228. package/tool_scripts/start_oysterun.sh +2 -0
  229. package/tool_scripts/sync_host_stack_config.mjs +23 -13
  230. package/tool_scripts/test_environment_remote_cloud_deprovision.mjs +13 -10
  231. package/dev/client/web-chat/dist/assets/SessionsSetupApp-CKJOSPFP.js +0 -6442
  232. package/dev/client/web-chat/dist/assets/SessionsSetupApp-Dx09Uz-6.css +0 -1
  233. package/dev/client/web-chat/dist/assets/matrix-entry-abAy3aOo.js.map +0 -1
  234. package/host-service/agent-store-demo.mjs +0 -369
  235. package/host-service/assets/product-skills/Oysterun/modules/oysterun-find-context/SKILL.md +0 -114
  236. package/host-service/assets/product-skills/Oysterun/modules/oysterun-find-context/scripts/oysterun_find_context.mjs +0 -49
  237. package/host-service/assets/product-skills/oysterun-find-context/SKILL.md +0 -114
  238. package/host-service/assets/product-skills/oysterun-find-context/scripts/oysterun_find_context.mjs +0 -49
  239. package/host-service/legacy-continuity-observation.mjs +0 -33
  240. package/static/site/docs/host-authentication-and-connection/assets/publication/sessions-connected-direct-host.png +0 -0
  241. package/static/site_source/content/tutorials/published/host-authentication-and-connection/assets/publication/sessions-connected-direct-host.png +0 -0
@@ -1258,48 +1258,6 @@
1258
1258
  justify-content: center;
1259
1259
  }
1260
1260
 
1261
- .agent-store-demo-backdrop {
1262
- z-index: 10020;
1263
- }
1264
-
1265
- .agent-store-demo-status {
1266
- min-height: 42px;
1267
- margin-top: 14px;
1268
- display: flex;
1269
- align-items: center;
1270
- gap: 10px;
1271
- color: var(--ink-soft);
1272
- font-size: 14px;
1273
- line-height: 1.45;
1274
- }
1275
-
1276
- .agent-store-demo-spinner {
1277
- width: 16px;
1278
- height: 16px;
1279
- flex: 0 0 16px;
1280
- border: 2px solid rgba(151, 89, 49, 0.22);
1281
- border-top-color: var(--accent);
1282
- border-radius: 50%;
1283
- animation: oysterun-auth-spin 800ms linear infinite;
1284
- }
1285
-
1286
- .browse-create-folder-modal .agent-store-demo-actions {
1287
- display: grid;
1288
- grid-template-columns: minmax(0, 1fr);
1289
- gap: 10px;
1290
- }
1291
-
1292
- .agent-store-demo-actions > button {
1293
- min-width: 0;
1294
- justify-content: center;
1295
- }
1296
-
1297
- #agent-store-demo-submit-btn {
1298
- display: inline-flex;
1299
- align-items: center;
1300
- gap: 9px;
1301
- }
1302
-
1303
1261
  #app {
1304
1262
  display: none;
1305
1263
  height: 100vh;
@@ -4749,6 +4707,57 @@
4749
4707
  grid-template-columns: minmax(0, 1fr);
4750
4708
  }
4751
4709
 
4710
+ /* Folder Session page: History rows with inline Resume / Duplicate Setup. */
4711
+ .session-list-row-history.session-list-row-inline-actions {
4712
+ grid-template-columns: minmax(0, 1fr) auto;
4713
+ align-items: center;
4714
+ }
4715
+
4716
+ .session-row-inline-actions {
4717
+ display: flex;
4718
+ align-items: center;
4719
+ padding-right: 8px;
4720
+ }
4721
+
4722
+ .session-row-inline-action {
4723
+ width: var(--oysterun-session-action-trigger-target-size);
4724
+ height: var(--oysterun-session-action-trigger-target-size);
4725
+ display: grid;
4726
+ place-items: center;
4727
+ padding: 0;
4728
+ border: 0;
4729
+ border-radius: 50%;
4730
+ background: transparent;
4731
+ cursor: pointer;
4732
+ }
4733
+
4734
+ .session-row-inline-action:hover:not(:disabled),
4735
+ .session-row-inline-action:focus-visible {
4736
+ background: var(--oysterun-session-action-hover);
4737
+ outline: none;
4738
+ }
4739
+
4740
+ .session-row-inline-action:focus-visible {
4741
+ outline: 2px solid var(--oysterun-session-action-focus);
4742
+ outline-offset: -2px;
4743
+ }
4744
+
4745
+ .session-row-inline-action:disabled {
4746
+ cursor: default;
4747
+ opacity: 0.45;
4748
+ }
4749
+
4750
+ .folder-sessions-intro {
4751
+ display: grid;
4752
+ gap: 14px;
4753
+ padding: 16px 22px 8px;
4754
+ }
4755
+
4756
+ .folder-sessions-label {
4757
+ font-size: 13px;
4758
+ font-weight: 600;
4759
+ }
4760
+
4752
4761
  .session-list-row-desired-open {
4753
4762
  grid-template-columns: minmax(0, 1fr) 58px;
4754
4763
  }
@@ -7796,6 +7805,15 @@
7796
7805
  grid-template-columns: minmax(0, 1fr);
7797
7806
  }
7798
7807
 
7808
+ .sessions-collection-list
7809
+ .session-list-row-history.session-list-row-inline-actions {
7810
+ grid-template-columns: minmax(0, 1fr) auto;
7811
+ }
7812
+
7813
+ .folder-sessions-intro {
7814
+ padding: 14px 14px 6px;
7815
+ }
7816
+
7799
7817
  .web-collection-list .web-site-row {
7800
7818
  grid-template-columns: minmax(0, 1fr) 86px;
7801
7819
  }
@@ -8488,26 +8506,6 @@
8488
8506
  >
8489
8507
  </div>
8490
8508
  <div class="topbar-right">
8491
- <button
8492
- class="sessions-compose-button topbar-agent-store hidden"
8493
- id="entry-agent-store-btn"
8494
- type="button"
8495
- aria-label="Agent Store"
8496
- title="Agent Store"
8497
- data-testid="oysterun-agent-store-demo"
8498
- onClick=${call("openAgentStoreDemo")}
8499
- >
8500
- <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
8501
- <path
8502
- d="M4 9.25h16v10.5H4V9.25Zm2-5h12l2 5H4l2-5Zm4 5v10.5m4-10.5v10.5"
8503
- fill="none"
8504
- stroke="currentColor"
8505
- stroke-width="1.7"
8506
- stroke-linecap="round"
8507
- stroke-linejoin="round"
8508
- ></path>
8509
- </svg>
8510
- </button>
8511
8509
  <button
8512
8510
  class="sessions-compose-button topbar-sessions-compose hidden"
8513
8511
  id="entry-start-session-btn"
@@ -9715,36 +9713,9 @@
9715
9713
  </div>
9716
9714
  </div>
9717
9715
  <div class="kv-table">
9718
- <div class="kv-label">Direct Host URL</div>
9719
- <div class="kv-value">
9720
- <div class="value-stack">
9721
- <div class="settings-copy-url-control">
9722
- <input
9723
- id="host-public-base-url"
9724
- type="url"
9725
- placeholder="Host URL from setup"
9726
- data-oysterun-p75-copyable-url-control="direct-host-url"
9727
- />
9728
- <button
9729
- class="btn-primary btn-compact settings-copy-url-btn"
9730
- id="host-public-base-url-copy-btn"
9731
- type="button"
9732
- onClick=${call(
9733
- "copyHostPreferencesDirectHostUrl"
9734
- )}
9735
- >
9736
- Copy
9737
- </button>
9738
- </div>
9739
- <div
9740
- class="field-hint"
9741
- id="host-public-base-url-hint"
9742
- >
9743
- Optional. Use this URL for manual connection.
9744
- </div>
9745
- </div>
9716
+ <div class="kv-label">
9717
+ Local address (this computer)
9746
9718
  </div>
9747
- <div class="kv-label">Local URL</div>
9748
9719
  <div class="kv-value">
9749
9720
  <div class="settings-copy-url-control">
9750
9721
  <input
@@ -10288,13 +10259,23 @@
10288
10259
  class="field-hint host-update-phase"
10289
10260
  id="host-update-phase"
10290
10261
  ></div>
10262
+ <button
10263
+ class="btn-ghost btn-compact hidden"
10264
+ type="button"
10265
+ id="host-update-card-diagnostics-btn"
10266
+ onClick=${call(
10267
+ "downloadHostLifecycleDiagnosticSummary"
10268
+ )}
10269
+ >
10270
+ Download diagnostic summary
10271
+ </button>
10291
10272
  <button
10292
10273
  class="btn-ghost btn-compact hidden"
10293
10274
  type="button"
10294
10275
  id="host-update-recovery-btn"
10295
10276
  onClick=${call("runOysterunRecoveryAction")}
10296
10277
  >
10297
- Repair and restart
10278
+ Restore and restart
10298
10279
  </button>
10299
10280
  <div
10300
10281
  class="field-hint host-update-recovery-status hidden"
@@ -10388,6 +10369,16 @@
10388
10369
  >
10389
10370
  Cancel
10390
10371
  </button>
10372
+ <button
10373
+ class="btn-ghost hidden"
10374
+ type="button"
10375
+ id="host-update-confirm-diagnostics-btn"
10376
+ onClick=${call(
10377
+ "downloadHostLifecycleDiagnosticSummary"
10378
+ )}
10379
+ >
10380
+ Download diagnostic summary
10381
+ </button>
10391
10382
  <button
10392
10383
  class="btn-primary"
10393
10384
  type="button"
@@ -11743,9 +11734,30 @@ Terminal output will appear here.</pre
11743
11734
  </div>
11744
11735
  </div>
11745
11736
  </div>
11737
+ </section>
11738
+ </div>
11739
+ </div>
11740
+ </section>
11741
+ `;
11742
+ }
11743
+
11744
+ // Resume Session reuses the Sessions history list primitives for one
11745
+ // folder. Its rows and menus come from the same History renderers.
11746
+ function FolderSessionsView() {
11747
+ return html`
11748
+ <section
11749
+ id="folder-sessions-view"
11750
+ class="view view-page-scroll"
11751
+ data-oysterun-folder-sessions-page="true"
11752
+ >
11753
+ <div class="collection-layout sessions-collection-layout">
11754
+ <div class="collection-shell sessions-collection-shell">
11755
+ <section
11756
+ class="panel collection-panel sessions-collection-panel"
11757
+ >
11746
11758
  <div
11747
- class="session-action-menu-layer hidden"
11748
- id="session-action-menu-layer"
11759
+ class="collection-list sessions-collection-list"
11760
+ id="folder-sessions-list"
11749
11761
  ></div>
11750
11762
  </section>
11751
11763
  </div>
@@ -12508,126 +12520,6 @@ Terminal output will appear here.</pre
12508
12520
  `;
12509
12521
  }
12510
12522
 
12511
- function AgentStoreDemoModal() {
12512
- return html`
12513
- <div
12514
- id="agent-store-demo-backdrop"
12515
- class="modal-backdrop sheet browse-create-folder-backdrop agent-store-demo-backdrop hidden"
12516
- onClick=${closeBackdrop("closeAgentStoreDemo")}
12517
- >
12518
- <div
12519
- class="modal sheet narrow browse-create-folder-modal agent-store-demo-modal"
12520
- role="dialog"
12521
- aria-modal="true"
12522
- aria-labelledby="agent-store-demo-title"
12523
- data-oysterun-agent-store-demo="true"
12524
- >
12525
- <button
12526
- class="modal-close-btn browse-create-folder-close-btn"
12527
- id="agent-store-demo-close-btn"
12528
- type="button"
12529
- aria-label="Close"
12530
- title="Close"
12531
- onClick=${call("closeAgentStoreDemo")}
12532
- >
12533
- x
12534
- </button>
12535
- <div class="modal-scroll">
12536
- <span class="eyebrow">Agent Store Demo</span>
12537
- <h2 id="agent-store-demo-title">Clone GitHub Agent</h2>
12538
- <div class="field" style="margin-top:14px">
12539
- <label for="agent-store-demo-repository-select"
12540
- >Demo Repository</label
12541
- >
12542
- <select
12543
- id="agent-store-demo-repository-select"
12544
- onChange=${(event) =>
12545
- window.handleAgentStoreDemoRepositorySelect?.(event)}
12546
- >
12547
- <option value="">Choose a demo repository</option>
12548
- <option
12549
- value="https://github.com/GoatWang/oysterun-ccstock-workenv"
12550
- >
12551
- CCStock WorkEnv
12552
- </option>
12553
- <option
12554
- value="https://github.com/GoatWang/oysterun-ccstock-workenv-en"
12555
- >
12556
- CCStock WorkEnv (English)
12557
- </option>
12558
- <option
12559
- value="https://github.com/GoatWang/oysterun-github-tracker"
12560
- >
12561
- GitHub Tracker
12562
- </option>
12563
- </select>
12564
- </div>
12565
- <div class="field" style="margin-top:14px">
12566
- <label for="agent-store-demo-url-input"
12567
- >GitHub Repository URL</label
12568
- >
12569
- <input
12570
- id="agent-store-demo-url-input"
12571
- type="url"
12572
- inputmode="url"
12573
- autocomplete="url"
12574
- autocapitalize="none"
12575
- spellcheck="false"
12576
- placeholder="https://github.com/owner/repository"
12577
- onInput=${(event) =>
12578
- window.handleAgentStoreDemoUrlInput?.(event)}
12579
- onKeyDown=${(event) =>
12580
- window.handleAgentStoreDemoUrlKeyDown?.(event)}
12581
- />
12582
- </div>
12583
- <div
12584
- id="agent-store-demo-status"
12585
- class="agent-store-demo-status hidden"
12586
- role="status"
12587
- aria-live="polite"
12588
- aria-atomic="true"
12589
- >
12590
- <span
12591
- id="agent-store-demo-status-spinner"
12592
- class="agent-store-demo-spinner"
12593
- aria-hidden="true"
12594
- ></span>
12595
- <span id="agent-store-demo-status-text"></span>
12596
- </div>
12597
- <div
12598
- id="agent-store-demo-error"
12599
- class="error-box hidden"
12600
- style="margin-top:12px"
12601
- ></div>
12602
- </div>
12603
- <div class="modal-actions agent-store-demo-actions">
12604
- <button
12605
- class="btn-primary"
12606
- id="agent-store-demo-submit-btn"
12607
- type="button"
12608
- onClick=${call("submitAgentStoreDemo")}
12609
- >
12610
- <span
12611
- id="agent-store-demo-submit-spinner"
12612
- class="agent-store-demo-spinner hidden"
12613
- aria-hidden="true"
12614
- ></span>
12615
- <span id="agent-store-demo-submit-label">OK</span>
12616
- </button>
12617
- <button
12618
- class="btn-secondary hidden"
12619
- id="agent-store-demo-session-setup-btn"
12620
- type="button"
12621
- onClick=${call("openAgentStoreDemoSessionSetup")}
12622
- >
12623
- Open Session Setup
12624
- </button>
12625
- </div>
12626
- </div>
12627
- </div>
12628
- `;
12629
- }
12630
-
12631
12523
  function ProviderAuthModal() {
12632
12524
  return html`
12633
12525
  <div
@@ -12811,6 +12703,7 @@ Waiting for Claude login output…</pre
12811
12703
  <div class="app-body">
12812
12704
  <main class="workspace" id="dashboard-workspace">
12813
12705
  <${EntryView} />
12706
+ <${FolderSessionsView} />
12814
12707
  <${WebView} />
12815
12708
  <${MailView} />
12816
12709
  <${SchedulerView} />
@@ -12826,6 +12719,10 @@ Waiting for Claude login output…</pre
12826
12719
  <${BrowserSurfaceView} />
12827
12720
  <${AgentProfileView} />
12828
12721
  <${ExplorerView} />
12722
+ <div
12723
+ class="session-action-menu-layer hidden"
12724
+ id="session-action-menu-layer"
12725
+ ></div>
12829
12726
  </main>
12830
12727
  </div>
12831
12728
  <nav class="bottom-nav" id="bottom-nav">
@@ -12981,7 +12878,6 @@ Waiting for Claude login output…</pre
12981
12878
  <div id="browse-context-menu" class="browse-context-menu hidden" role="menu" aria-hidden="true" tabindex="-1"></div>
12982
12879
  <div id="app-toast" class="app-toast oysterun-notice hidden" role="status" aria-live="polite" aria-atomic="true"></div>
12983
12880
  <${BrowseCreateFolderModal} />
12984
- <${AgentStoreDemoModal} />
12985
12881
  <${ProviderAuthModal} />
12986
12882
  <${ChatImagePreviewModal} />
12987
12883
  <${HiddenCache} />
@@ -14454,7 +14350,6 @@ Waiting for Claude login output…</pre
14454
14350
  const OYSTERUN_PROVIDER_SKILL_MODULE_HELPERS = Object.freeze([
14455
14351
  ["oysterun_sessions_skill", "oysterun-sessions"],
14456
14352
  ["oysterun_session_chat_skill", "oysterun-session-chat"],
14457
- ["oysterun_find_context_skill", "oysterun-find-context"],
14458
14353
  ["oysterun_scheduler_skill", "oysterun-scheduler"],
14459
14354
  ["oysterun_mail_skill", "oysterun-mail"],
14460
14355
  ["oysterun_notification_skill", "oysterun-notifications"],
@@ -14689,6 +14584,7 @@ Waiting for Claude login output…</pre
14689
14584
  for (const field of [
14690
14585
  "restore_failed_reason",
14691
14586
  "manual_start_setup_reason",
14587
+ "agent_folder_real_path",
14692
14588
  ]) {
14693
14589
  if (field in row) nullableString(row[field], field);
14694
14590
  }
@@ -14735,6 +14631,16 @@ Waiting for Claude login output…</pre
14735
14631
  "Session History cannot claim active provider lifecycle."
14736
14632
  );
14737
14633
  }
14634
+ if (
14635
+ row.telegram_listener_alert != null &&
14636
+ (typeof row.telegram_listener_alert !== "object" ||
14637
+ typeof row.telegram_listener_alert.outage_id !== "string" ||
14638
+ row.kind !== "running")
14639
+ ) {
14640
+ throw new Error(
14641
+ "Session data Telegram listener reminder must belong to a live Session."
14642
+ );
14643
+ }
14738
14644
  if (
14739
14645
  "restore_failed" in row &&
14740
14646
  typeof row.restore_failed !== "boolean"
@@ -16673,17 +16579,6 @@ Waiting for Claude login output…</pre
16673
16579
  };
16674
16580
  }
16675
16581
 
16676
- function createAgentStoreDemoState() {
16677
- return {
16678
- visible: false,
16679
- phase: "idle",
16680
- repositoryUrl: "",
16681
- status: "",
16682
- error: "",
16683
- clone: null,
16684
- requestId: 0,
16685
- };
16686
- }
16687
16582
  function createTerminalViewState() {
16688
16583
  return {
16689
16584
  terminal: null,
@@ -16750,18 +16645,6 @@ Waiting for Claude login output…</pre
16750
16645
  payload.display_name = String(value || "").trim() || null;
16751
16646
  },
16752
16647
  }),
16753
- hostPreferenceField("publicBaseUrl", "", {
16754
- controlIds: ["host-public-base-url"],
16755
- read: hostPreferenceTextControl(
16756
- "host-public-base-url",
16757
- "publicBaseUrl"
16758
- ),
16759
- serialize: (value, { payload }) => {
16760
- const normalized = String(value || "").trim() || null;
16761
- payload.direct_host_url = normalized;
16762
- payload.public_base_url = normalized;
16763
- },
16764
- }),
16765
16648
  hostPreferenceField("defaultBrowsePath", "", {
16766
16649
  controlIds: ["host-default-browse-path"],
16767
16650
  read: hostPreferenceTextControl(
@@ -16993,9 +16876,6 @@ Waiting for Claude login output…</pre
16993
16876
  loaded: false,
16994
16877
  loading: false,
16995
16878
  saving: false,
16996
- // Internal cloud/mamarun plumbing is retained but not product-facing
16997
- // in the short term; Host Preferences intentionally hides this.
16998
- connectionMode: "direct",
16999
16879
  displayName: "",
17000
16880
  notificationSoundWebEnabled: true,
17001
16881
  notificationSoundAppEnabled: true,
@@ -17045,8 +16925,6 @@ Waiting for Claude login output…</pre
17045
16925
  allowedPathDisabled: true,
17046
16926
  allowedPathDebugUiEnabled: false,
17047
16927
  providerPermissionDebugModeDropdownEnabled: false,
17048
- publicBaseUrl: "",
17049
- activeDirectHostUrl: "",
17050
16928
  managedTunnelEnabled: false,
17051
16929
  managedTunnelStatus: "disabled",
17052
16930
  managedTunnelStatusReason: "managed_tunnel_disabled",
@@ -17060,7 +16938,6 @@ Waiting for Claude login output…</pre
17060
16938
  notificationDiagnosticsUiEnabled: false,
17061
16939
  notificationServiceWorkerState: "unknown",
17062
16940
  notificationLastError: "",
17063
- directHostUrlRestartRequired: false,
17064
16941
  hostRestartSupported: false,
17065
16942
  loginQr: null,
17066
16943
  loginQrLoading: false,
@@ -17148,7 +17025,6 @@ Waiting for Claude login output…</pre
17148
17025
  },
17149
17026
  };
17150
17027
  let providerAuthModalState = createProviderAuthModalState();
17151
- let agentStoreDemoState = createAgentStoreDemoState();
17152
17028
  let chatImagePreviewState = {
17153
17029
  visible: false,
17154
17030
  requestKey: "",
@@ -17624,16 +17500,6 @@ Waiting for Claude login output…</pre
17624
17500
  typeof fallbackValue === "string" ? fallbackValue.trim() : "";
17625
17501
  return fallback || defaultValue;
17626
17502
  }
17627
- function requiresProviderPermissionPolicyConfirmation(providerId, value) {
17628
- const selected = typeof value === "string" ? value.trim() : "";
17629
- const defaultValue = getDefaultProviderPermissionPolicy(providerId);
17630
- return (
17631
- isProviderPermissionDebugModeDropdownEnabled() &&
17632
- Boolean(selected) &&
17633
- Boolean(defaultValue) &&
17634
- selected !== defaultValue
17635
- );
17636
- }
17637
17503
  function syncSessionSetupInterfaceStyleVisibility() {
17638
17504
  const visible = isSessionSetupInterfaceStyleVisible();
17639
17505
  for (const element of document.querySelectorAll(
@@ -22374,6 +22240,7 @@ Waiting for Claude login output…</pre
22374
22240
  syncOwnerId = null,
22375
22241
  timeoutMs = void 0,
22376
22242
  onUploadProgress = null,
22243
+ onDispatch = null,
22377
22244
  } = {}
22378
22245
  ) {
22379
22246
  if (this.closed)
@@ -22390,6 +22257,7 @@ Waiting for Claude login output…</pre
22390
22257
  const isMatrixSync = MATRIX_SYNC_PATH.test(request.path);
22391
22258
  const startedAt = Date.now();
22392
22259
  const releaseTransport = this.acquireTransportLease(transport);
22260
+ onDispatch?.({ pathKind });
22393
22261
  this.emitDiagnostic({
22394
22262
  event_type: isMatrixSync
22395
22263
  ? "matrix_sync_request_started"
@@ -24715,6 +24583,17 @@ Waiting for Claude login output…</pre
24715
24583
  headers: response.headers || {},
24716
24584
  });
24717
24585
  }
24586
+ function responseFromDispatchedLogicalResult(
24587
+ result,
24588
+ dispatchedPathKind
24589
+ ) {
24590
+ return dispatchedPathKind === "webrtc"
24591
+ ? responseFromLogicalResult(result)
24592
+ : responseFromBootstrapLogicalResult(result);
24593
+ }
24594
+ function transportAccountingKind(pathKind) {
24595
+ return pathKind === "webrtc" ? "datachannel" : "bootstrap";
24596
+ }
24718
24597
  function logicalResultBodyByteLength(result) {
24719
24598
  if (result?.response?.body instanceof Uint8Array) {
24720
24599
  return result.response.body.byteLength;
@@ -26415,7 +26294,7 @@ Waiting for Claude login output…</pre
26415
26294
  }
26416
26295
  : {}),
26417
26296
  };
26418
- recordTransportRequest(this.accounting, "bootstrap", path);
26297
+ let dispatchedPathKind2 = "bootstrap";
26419
26298
  let result2;
26420
26299
  try {
26421
26300
  result2 = await this.logicalConnection.request(
@@ -26425,6 +26304,14 @@ Waiting for Claude login output…</pre
26425
26304
  syncOwnerId: MATRIX_SYNC_PATH2.test(path)
26426
26305
  ? this.ensureSyncOwner()
26427
26306
  : null,
26307
+ onDispatch: ({ pathKind }) => {
26308
+ dispatchedPathKind2 = pathKind;
26309
+ recordTransportRequest(
26310
+ this.accounting,
26311
+ transportAccountingKind(pathKind),
26312
+ path
26313
+ );
26314
+ },
26428
26315
  }
26429
26316
  );
26430
26317
  } catch (error) {
@@ -26465,10 +26352,13 @@ Waiting for Claude login output…</pre
26465
26352
  }
26466
26353
  throw error;
26467
26354
  }
26468
- const response = responseFromBootstrapLogicalResult(result2);
26355
+ const response = responseFromDispatchedLogicalResult(
26356
+ result2,
26357
+ dispatchedPathKind2
26358
+ );
26469
26359
  recordTransportResponseBytes(
26470
26360
  this.accounting,
26471
- "bootstrap",
26361
+ transportAccountingKind(dispatchedPathKind2),
26472
26362
  logicalResultBodyByteLength(result2),
26473
26363
  path
26474
26364
  );
@@ -26492,6 +26382,7 @@ Waiting for Claude login output…</pre
26492
26382
  signal: request.signal,
26493
26383
  ...(body ? { body } : {}),
26494
26384
  };
26385
+ let dispatchedPathKind = "webrtc";
26495
26386
  const result = await this.logicalConnection.request(
26496
26387
  requestPayload,
26497
26388
  {
@@ -26505,16 +26396,23 @@ Waiting for Claude login output…</pre
26505
26396
  ? BULK_REQUEST_TIMEOUT_MS
26506
26397
  : void 0,
26507
26398
  onUploadProgress: recovery.onUploadProgress || null,
26399
+ onDispatch: ({ pathKind }) => {
26400
+ dispatchedPathKind = pathKind;
26401
+ },
26508
26402
  }
26509
26403
  );
26510
- recordTransportRequest(this.accounting, "datachannel", path);
26404
+ const accountingKind = transportAccountingKind(dispatchedPathKind);
26405
+ recordTransportRequest(this.accounting, accountingKind, path);
26511
26406
  recordTransportResponseBytes(
26512
26407
  this.accounting,
26513
- "datachannel",
26408
+ accountingKind,
26514
26409
  logicalResultBodyByteLength(result),
26515
26410
  path
26516
26411
  );
26517
- return responseFromLogicalResult(result);
26412
+ return responseFromDispatchedLogicalResult(
26413
+ result,
26414
+ dispatchedPathKind
26415
+ );
26518
26416
  }
26519
26417
  status() {
26520
26418
  const logicalStatus = this.logicalConnection.status();
@@ -26770,7 +26668,7 @@ Waiting for Claude login output…</pre
26770
26668
  ["closed", "disconnected", "recovering", "unavailable"].includes(
26771
26669
  status.connection_state
26772
26670
  );
26773
- let qualifier = "Direct Host";
26671
+ let qualifier = "Local address";
26774
26672
  if (originKind === "managed_endpoint") {
26775
26673
  qualifier =
26776
26674
  activePathKind === "webrtc"
@@ -27682,376 +27580,6 @@ Waiting for Claude login output…</pre
27682
27580
  window.openExplorerFromSessionsEmptyState =
27683
27581
  openExplorerFromSessionsEmptyState;
27684
27582
 
27685
- const AGENT_STORE_DEMO_PENDING_PHASES = new Set([
27686
- "validating",
27687
- "cloning",
27688
- "loading_config",
27689
- "starting_session",
27690
- ]);
27691
-
27692
- const AGENT_STORE_DEMO_REPOSITORY_URLS = new Set([
27693
- "https://github.com/GoatWang/oysterun-ccstock-workenv",
27694
- "https://github.com/GoatWang/oysterun-ccstock-workenv-en",
27695
- "https://github.com/GoatWang/oysterun-github-tracker",
27696
- ]);
27697
-
27698
- function isAgentStoreDemoPending() {
27699
- return AGENT_STORE_DEMO_PENDING_PHASES.has(agentStoreDemoState.phase);
27700
- }
27701
-
27702
- function getAgentStoreDemoPendingLabel() {
27703
- if (agentStoreDemoState.phase === "validating") return "Validating...";
27704
- if (agentStoreDemoState.phase === "cloning") return "Cloning...";
27705
- if (agentStoreDemoState.phase === "loading_config")
27706
- return "Loading config...";
27707
- if (agentStoreDemoState.phase === "starting_session")
27708
- return "Starting session...";
27709
- return "OK";
27710
- }
27711
-
27712
- function renderAgentStoreDemo() {
27713
- const backdrop = $("agent-store-demo-backdrop");
27714
- if (!backdrop) return;
27715
- const pending = isAgentStoreDemoPending();
27716
- const cloneComplete = Boolean(agentStoreDemoState.clone?.agent_folder);
27717
- const input = $("agent-store-demo-url-input");
27718
- const repositorySelect = $("agent-store-demo-repository-select");
27719
- const closeButton = $("agent-store-demo-close-btn");
27720
- const submitButton = $("agent-store-demo-submit-btn");
27721
- const submitLabel = $("agent-store-demo-submit-label");
27722
-
27723
- backdrop.classList.toggle(
27724
- "hidden",
27725
- agentStoreDemoState.visible !== true
27726
- );
27727
- backdrop.dataset.phase = agentStoreDemoState.phase;
27728
- if (input) {
27729
- if (input.value !== agentStoreDemoState.repositoryUrl) {
27730
- input.value = agentStoreDemoState.repositoryUrl;
27731
- }
27732
- input.disabled = pending || cloneComplete;
27733
- }
27734
- if (repositorySelect) {
27735
- repositorySelect.value = AGENT_STORE_DEMO_REPOSITORY_URLS.has(
27736
- agentStoreDemoState.repositoryUrl
27737
- )
27738
- ? agentStoreDemoState.repositoryUrl
27739
- : "";
27740
- repositorySelect.disabled = pending || cloneComplete;
27741
- }
27742
- if (closeButton) closeButton.disabled = pending;
27743
- if (submitButton) {
27744
- submitButton.disabled =
27745
- pending ||
27746
- cloneComplete ||
27747
- !agentStoreDemoState.repositoryUrl.trim();
27748
- submitButton.classList.toggle(
27749
- "hidden",
27750
- agentStoreDemoState.phase === "failed_after_clone"
27751
- );
27752
- }
27753
- if (submitLabel)
27754
- submitLabel.textContent = getAgentStoreDemoPendingLabel();
27755
- setVisible("agent-store-demo-submit-spinner", pending);
27756
- setVisible("agent-store-demo-status", pending);
27757
- setText(
27758
- "agent-store-demo-status-text",
27759
- agentStoreDemoState.status || ""
27760
- );
27761
- showError("agent-store-demo-error", agentStoreDemoState.error || "");
27762
- setVisible(
27763
- "agent-store-demo-session-setup-btn",
27764
- agentStoreDemoState.phase === "failed_after_clone" && cloneComplete
27765
- );
27766
- }
27767
-
27768
- function openAgentStoreDemo() {
27769
- if (isAgentStoreDemoPending()) return;
27770
- agentStoreDemoState = {
27771
- ...createAgentStoreDemoState(),
27772
- visible: true,
27773
- requestId: agentStoreDemoState.requestId + 1,
27774
- };
27775
- renderAgentStoreDemo();
27776
- requestAnimationFrame(() => {
27777
- $("agent-store-demo-url-input")?.focus({ preventScroll: true });
27778
- });
27779
- }
27780
-
27781
- function closeAgentStoreDemo(opts = {}) {
27782
- if (isAgentStoreDemoPending() && opts.force !== true) return;
27783
- agentStoreDemoState = {
27784
- ...createAgentStoreDemoState(),
27785
- requestId: agentStoreDemoState.requestId + 1,
27786
- };
27787
- renderAgentStoreDemo();
27788
- }
27789
-
27790
- function handleAgentStoreDemoUrlInput(event) {
27791
- if (isAgentStoreDemoPending() || agentStoreDemoState.clone) return;
27792
- agentStoreDemoState = {
27793
- ...agentStoreDemoState,
27794
- phase: "idle",
27795
- repositoryUrl: event?.target?.value || "",
27796
- status: "",
27797
- error: "",
27798
- };
27799
- renderAgentStoreDemo();
27800
- }
27801
-
27802
- function handleAgentStoreDemoRepositorySelect(event) {
27803
- if (isAgentStoreDemoPending() || agentStoreDemoState.clone) return;
27804
- const repositoryUrl = String(event?.target?.value || "").trim();
27805
- if (!repositoryUrl) return;
27806
- agentStoreDemoState = {
27807
- ...agentStoreDemoState,
27808
- phase: "idle",
27809
- repositoryUrl,
27810
- status: "",
27811
- error: "",
27812
- };
27813
- renderAgentStoreDemo();
27814
- requestAnimationFrame(() => {
27815
- $("agent-store-demo-url-input")?.focus({ preventScroll: true });
27816
- });
27817
- }
27818
-
27819
- function handleAgentStoreDemoUrlKeyDown(event) {
27820
- if (event?.key !== "Enter" || event.isComposing) return;
27821
- event.preventDefault();
27822
- if (!isAgentStoreDemoPending()) void submitAgentStoreDemo();
27823
- }
27824
-
27825
- function validateAgentStoreDemoUrl(value) {
27826
- let parsed;
27827
- try {
27828
- parsed = new URL(String(value || "").trim());
27829
- } catch {
27830
- throw new Error("Enter a valid public GitHub HTTPS repository URL.");
27831
- }
27832
- if (
27833
- parsed.protocol !== "https:" ||
27834
- parsed.hostname.toLowerCase() !== "github.com" ||
27835
- parsed.username ||
27836
- parsed.password ||
27837
- parsed.port ||
27838
- parsed.search ||
27839
- parsed.hash ||
27840
- !/^\/[^/]+\/[^/]+\/?$/.test(parsed.pathname)
27841
- ) {
27842
- throw new Error(
27843
- "Only public https://github.com/<owner>/<repo> URLs are supported."
27844
- );
27845
- }
27846
- return parsed.toString();
27847
- }
27848
-
27849
- async function getAgentStoreDemoProviderSkillInstallDecision({
27850
- agentId,
27851
- agentFolder,
27852
- provider,
27853
- }) {
27854
- const params = new URLSearchParams({
27855
- agent_id: agentId,
27856
- agent_folder: agentFolder,
27857
- provider,
27858
- });
27859
- try {
27860
- const status = await apiWithAuthRetry(
27861
- "GET",
27862
- `/agent/provider-skill-status?${params.toString()}`
27863
- );
27864
- return (
27865
- status?.default_checked === true &&
27866
- status?.can_install === true &&
27867
- status?.disabled !== true
27868
- );
27869
- } catch {
27870
- return false;
27871
- }
27872
- }
27873
-
27874
- function buildAgentStoreDemoSessionStartPayload({
27875
- clone,
27876
- folderConfig,
27877
- installOysterunSkills,
27878
- }) {
27879
- const provider = String(folderConfig?.provider || "").trim();
27880
- const model = String(folderConfig?.model || "").trim();
27881
- if (!provider || !model) {
27882
- throw new Error(
27883
- "The cloned folder did not resolve a provider and model."
27884
- );
27885
- }
27886
- const payload = {
27887
- agent_id: clone.agent_id,
27888
- agent_folder: clone.agent_folder,
27889
- provider,
27890
- model,
27891
- interface_type: normalizeFirstPartyInterfaceType(
27892
- folderConfig.interfaceType || "coding"
27893
- ),
27894
- reasoning_effort: folderConfig.reasoningEffort || undefined,
27895
- dangerous_mode: folderConfig.dangerousMode === true,
27896
- allow_dangerously_skip_permissions:
27897
- folderConfig.allowDangerouslySkipPermissions === true,
27898
- install_oysterun_skills: installOysterunSkills === true,
27899
- };
27900
- if (provider === "claude") {
27901
- payload.permission_mode = folderConfig.permissionMode;
27902
- } else if (provider === "codex") {
27903
- payload.approval_policy = folderConfig.approvalPolicy;
27904
- }
27905
- const proof = {
27906
- ...buildCreateSessionSetupProviderModelPermissionProof(payload),
27907
- proof_surface: "visible_agent_store_clone_modal_state",
27908
- };
27909
- payload.session_setup_provider_model_permission_proof = proof;
27910
- return { payload, proof };
27911
- }
27912
-
27913
- function finalizeAgentStoreDemoStartedSession(data, expectedProof) {
27914
- const proofValidation =
27915
- validateSessionSetupProviderModelPermissionProofResponse(
27916
- data,
27917
- expectedProof,
27918
- { requireCountableStartResponseProof: true }
27919
- );
27920
- if (!proofValidation.ok) {
27921
- throw new Error(
27922
- `Session Setup proof validation failed: ${proofValidation.errors.join(
27923
- " "
27924
- )}`
27925
- );
27926
- }
27927
- const normalizedData = {
27928
- ...data,
27929
- session_setup_provider_model_permission_proof: proofValidation.proof,
27930
- };
27931
- clearPendingAttachments();
27932
- primeSelectedAgentFromStart(normalizedData);
27933
- const startedLiveSession =
27934
- upsertStartedSessionIntoLiveCatalog(normalizedData);
27935
- invalidateDashboardDataCaches({
27936
- catalog: true,
27937
- sessions: true,
27938
- history: true,
27939
- transcripts: true,
27940
- });
27941
- if (
27942
- !startedLiveSession ||
27943
- startedLiveSession.sessionId !== normalizedData.session_id
27944
- ) {
27945
- throw new Error(
27946
- "Agent Store could not materialize the started Host session before opening chat."
27947
- );
27948
- }
27949
- return normalizedData.session_id;
27950
- }
27951
-
27952
- async function submitAgentStoreDemo() {
27953
- if (isAgentStoreDemoPending() || agentStoreDemoState.clone) return;
27954
- const requestId = agentStoreDemoState.requestId + 1;
27955
- agentStoreDemoState = {
27956
- ...agentStoreDemoState,
27957
- phase: "validating",
27958
- status: "Validating GitHub repository...",
27959
- error: "",
27960
- requestId,
27961
- };
27962
- renderAgentStoreDemo();
27963
-
27964
- try {
27965
- const repositoryUrl = validateAgentStoreDemoUrl(
27966
- agentStoreDemoState.repositoryUrl
27967
- );
27968
- agentStoreDemoState = {
27969
- ...agentStoreDemoState,
27970
- phase: "cloning",
27971
- status: "Cloning the repository into Default Browse Root...",
27972
- };
27973
- renderAgentStoreDemo();
27974
- const clone = await apiWithAuthRetry(
27975
- "POST",
27976
- "/agent-store/demo/clone",
27977
- { repository_url: repositoryUrl }
27978
- );
27979
- if (agentStoreDemoState.requestId !== requestId) return;
27980
-
27981
- agentStoreDemoState = {
27982
- ...agentStoreDemoState,
27983
- phase: "loading_config",
27984
- status: "Loading the cloned agent configuration...",
27985
- clone,
27986
- };
27987
- renderAgentStoreDemo();
27988
- const folderConfig = await apiWithAuthRetry(
27989
- "GET",
27990
- `/dev/folder-config?path=${encodeURIComponent(clone.agent_folder)}`
27991
- );
27992
- if (agentStoreDemoState.requestId !== requestId) return;
27993
- const installOysterunSkills =
27994
- await getAgentStoreDemoProviderSkillInstallDecision({
27995
- agentId: clone.agent_id,
27996
- agentFolder: clone.agent_folder,
27997
- provider: folderConfig.provider,
27998
- });
27999
- const { payload, proof } = buildAgentStoreDemoSessionStartPayload({
28000
- clone,
28001
- folderConfig,
28002
- installOysterunSkills,
28003
- });
28004
-
28005
- agentStoreDemoState = {
28006
- ...agentStoreDemoState,
28007
- phase: "starting_session",
28008
- status: "Starting a session with the resolved agent settings...",
28009
- };
28010
- renderAgentStoreDemo();
28011
- const data = await apiWithAuthRetry(
28012
- "POST",
28013
- "/session/start",
28014
- payload
28015
- );
28016
- if (agentStoreDemoState.requestId !== requestId) return;
28017
- const sessionId = finalizeAgentStoreDemoStartedSession(data, proof);
28018
- closeAgentStoreDemo({ force: true });
28019
- openRouteCForStartedSession(sessionId);
28020
- } catch (err) {
28021
- if (agentStoreDemoState.requestId !== requestId) return;
28022
- const cloneComplete = Boolean(
28023
- agentStoreDemoState.clone?.agent_folder
28024
- );
28025
- const detail = String(err?.data?.detail || "").trim();
28026
- const message = cloneComplete
28027
- ? formatCreateStepError("Could not start session", err)
28028
- : err?.message || "Could not clone the GitHub repository.";
28029
- agentStoreDemoState = {
28030
- ...agentStoreDemoState,
28031
- phase: cloneComplete ? "failed_after_clone" : "failed_before_clone",
28032
- status: "",
28033
- error: detail ? `${message}\n${detail}` : message,
28034
- };
28035
- renderAgentStoreDemo();
28036
- }
28037
- }
28038
-
28039
- function openAgentStoreDemoSessionSetup() {
28040
- const folder = agentStoreDemoState.clone?.agent_folder || "";
28041
- if (!folder || isAgentStoreDemoPending()) return;
28042
- closeAgentStoreDemo({ force: true });
28043
- openCreateModal(folder);
28044
- }
28045
-
28046
- window.openAgentStoreDemo = openAgentStoreDemo;
28047
- window.closeAgentStoreDemo = closeAgentStoreDemo;
28048
- window.handleAgentStoreDemoUrlInput = handleAgentStoreDemoUrlInput;
28049
- window.handleAgentStoreDemoRepositorySelect =
28050
- handleAgentStoreDemoRepositorySelect;
28051
- window.handleAgentStoreDemoUrlKeyDown = handleAgentStoreDemoUrlKeyDown;
28052
- window.submitAgentStoreDemo = submitAgentStoreDemo;
28053
- window.openAgentStoreDemoSessionSetup = openAgentStoreDemoSessionSetup;
28054
-
28055
27583
  function getHostDefaultRuntime() {
28056
27584
  return (
28057
27585
  hostPreferencesState.effectiveDefaultProvider ||
@@ -30099,10 +29627,6 @@ Waiting for Claude login output…</pre
30099
29627
  );
30100
29628
  }
30101
29629
 
30102
- function getConnectionModeLabel(mode) {
30103
- return mode === "cloud" ? "Cloud" : "Direct";
30104
- }
30105
-
30106
29630
  function getCreateDefaultInterfaceType() {
30107
29631
  return normalizeFirstPartyInterfaceType(
30108
29632
  hostPreferencesState.interfaceType ||
@@ -31044,6 +30568,36 @@ Waiting for Claude login output…</pre
31044
30568
  });
31045
30569
  }
31046
30570
 
30571
+ function buildFolderSessionsRoute({ folder = "", returnPath = "" } = {}) {
30572
+ return normalizeClientRoute({
30573
+ screen: "folder-sessions",
30574
+ folder,
30575
+ returnPath,
30576
+ });
30577
+ }
30578
+
30579
+ // Resume Session returns only to the standalone Explorer location that
30580
+ // opened it, like File Preview and the folder picker's return_path.
30581
+ function normalizeFolderSessionsReturnPath(value) {
30582
+ const rawPath = sanitizeRouteText(value);
30583
+ if (!rawPath) return "";
30584
+ let resolvedPath;
30585
+ try {
30586
+ resolvedPath = new URL(rawPath, BASE);
30587
+ } catch {
30588
+ return "";
30589
+ }
30590
+ if (resolvedPath.origin !== BASE) return "";
30591
+ const pathname = resolvedPath.pathname.replace(/\/+$/, "") || "/";
30592
+ if (pathname !== "/app/explorer") return "";
30593
+ return `${pathname}${resolvedPath.search}`;
30594
+ }
30595
+
30596
+ function getFolderSessionsReturnTarget(route = getCurrentRoute()) {
30597
+ // Same fallback as File Preview: the standalone Explorer.
30598
+ return normalizeClientRoute(route).returnPath || "/app/explorer";
30599
+ }
30600
+
31047
30601
  function buildSettingsRoute() {
31048
30602
  return normalizeClientRoute({ screen: "host-preferences" });
31049
30603
  }
@@ -31319,6 +30873,22 @@ Waiting for Claude login output…</pre
31319
30873
  warning,
31320
30874
  };
31321
30875
  }
30876
+ if (screen === "folder-sessions") {
30877
+ const folder = sanitizeRouteText(route?.folder);
30878
+ if (!folder) {
30879
+ return buildSessionsRoute({
30880
+ warning:
30881
+ warning ||
30882
+ "Requested Resume Session page is missing a folder. Opening Sessions.",
30883
+ });
30884
+ }
30885
+ return {
30886
+ screen: "folder-sessions",
30887
+ folder,
30888
+ returnPath: normalizeFolderSessionsReturnPath(route?.returnPath),
30889
+ warning,
30890
+ };
30891
+ }
31322
30892
  if (screen === "host-preferences") {
31323
30893
  return { screen: "host-preferences", warning };
31324
30894
  }
@@ -31554,6 +31124,12 @@ Waiting for Claude login output…</pre
31554
31124
  url.searchParams.get("mode") === "branch" ? "branch" : "",
31555
31125
  });
31556
31126
  }
31127
+ if (pathname === "/app/folder-sessions") {
31128
+ return buildFolderSessionsRoute({
31129
+ folder: url.searchParams.get("folder") || "",
31130
+ returnPath: url.searchParams.get("return_path") || "",
31131
+ });
31132
+ }
31557
31133
  if (pathname === "/app/settings") {
31558
31134
  return buildSettingsRoute();
31559
31135
  }
@@ -31687,6 +31263,12 @@ Waiting for Claude login output…</pre
31687
31263
  }
31688
31264
  }
31689
31265
  }
31266
+ } else if (normalized.screen === "folder-sessions") {
31267
+ url.pathname = "/app/folder-sessions";
31268
+ url.searchParams.set("folder", normalized.folder);
31269
+ if (normalized.returnPath) {
31270
+ url.searchParams.set("return_path", normalized.returnPath);
31271
+ }
31690
31272
  } else if (normalized.screen === "host-preferences") {
31691
31273
  url.pathname = "/app/settings";
31692
31274
  } else if (normalized.screen === "voice-input-usage") {
@@ -31978,6 +31560,11 @@ Waiting for Claude login output…</pre
31978
31560
  } else if (normalized.screen === "voice-input-usage") {
31979
31561
  renderHostVoiceInputUsageDetail();
31980
31562
  await loadHostVoiceInputUsage();
31563
+ } else if (normalized.screen === "folder-sessions") {
31564
+ // Each page opening re-reads Resume eligibility, like a menu opening.
31565
+ folderSessionsResumeStates = new Map();
31566
+ renderFolderSessionsView();
31567
+ void hydrateSessionListPreviews();
31981
31568
  } else if (normalized.screen === "change-password") {
31982
31569
  renderChangePasswordView();
31983
31570
  } else if (normalized.screen === "terminal") {
@@ -35307,9 +34894,34 @@ Waiting for Claude login output…</pre
35307
34894
  const rollbackText = operation.rollback_result?.safe_message || "";
35308
34895
  const recoveryText = operation.recovery_result?.safe_message || "";
35309
34896
  const cleanupText = operation.cleanup_result?.safe_message || "";
35310
- return `Failed check: ${rootFailure.check} (stage: ${
35311
- rootFailure.stage || "lifecycle"
35312
- }). ${rootFailure.safe_message} ${restartText}${
34897
+ // A repeated recovery attempt reports its own (latest) failure first;
34898
+ // the first failure stays visible as the root cause.
34899
+ const latestFailure = operation.latest_failure;
34900
+ const latestIsLater =
34901
+ latestFailure?.check &&
34902
+ latestFailure?.safe_message &&
34903
+ Date.parse(latestFailure.observed_at || "") >
34904
+ Date.parse(rootFailure.observed_at || "");
34905
+ const failureText = latestIsLater
34906
+ ? `Latest attempt failed${(() => {
34907
+ const at = new Date(latestFailure.observed_at);
34908
+ return Number.isNaN(at.getTime())
34909
+ ? ""
34910
+ : ` at ${at.toLocaleTimeString([], {
34911
+ hour: "numeric",
34912
+ minute: "2-digit",
34913
+ })}`;
34914
+ })()}: ${latestFailure.check} (stage: ${
34915
+ latestFailure.stage || "lifecycle"
34916
+ }). ${String(latestFailure.safe_message)
34917
+ .trim()
34918
+ .replace(/([^.!?])$/, "$1.")} First failure: ${
34919
+ rootFailure.check
34920
+ } (stage: ${rootFailure.stage || "lifecycle"}).`
34921
+ : `Failed check: ${rootFailure.check} (stage: ${
34922
+ rootFailure.stage || "lifecycle"
34923
+ }). ${rootFailure.safe_message}`;
34924
+ return `${failureText} ${restartText}${
35313
34925
  rollbackText ? ` ${rollbackText}` : ""
35314
34926
  }${recoveryText ? ` ${recoveryText}` : ""}${
35315
34927
  cleanupText ? ` ${cleanupText}` : ""
@@ -36767,28 +36379,6 @@ Waiting for Claude login output…</pre
36767
36379
  }
36768
36380
  }
36769
36381
 
36770
- async function copyHostPreferencesDirectHostUrl() {
36771
- const value = String(
36772
- $("host-public-base-url")?.value ||
36773
- hostPreferencesState.publicBaseUrl ||
36774
- ""
36775
- ).trim();
36776
- if (!value) {
36777
- showToast("Direct Host URL is unavailable.", true, 3200);
36778
- return;
36779
- }
36780
- try {
36781
- await writeTextToClipboard(value);
36782
- showToast("Copied Direct Host URL");
36783
- } catch (err) {
36784
- showToast(
36785
- err.message || "Could not copy Direct Host URL.",
36786
- true,
36787
- 4200
36788
- );
36789
- }
36790
- }
36791
-
36792
36382
  async function copyHostPreferencesManagedEndpoint() {
36793
36383
  const value = String(
36794
36384
  $("host-managed-endpoint")?.value ||
@@ -40803,6 +40393,12 @@ Waiting for Claude login output…</pre
40803
40393
  subtitle: "",
40804
40394
  };
40805
40395
  }
40396
+ if (screen === "folder-sessions") {
40397
+ return {
40398
+ title: getPathLastComponent(normalized.folder) || normalized.folder,
40399
+ subtitle: "",
40400
+ };
40401
+ }
40806
40402
  if (screen === "change-password") {
40807
40403
  return {
40808
40404
  title: "Change Password",
@@ -40896,7 +40492,6 @@ Waiting for Claude login output…</pre
40896
40492
  setVisible("browser-site-next-btn", isBrowser);
40897
40493
  setVisible("browser-site-share-btn", isBrowser);
40898
40494
  setVisible("browser-site-exit-btn", isBrowser);
40899
- setVisible("entry-agent-store-btn", screen === "sessions");
40900
40495
  setVisible("entry-start-session-btn", screen === "sessions");
40901
40496
  setVisible("topbar-scheduler-new-btn", isSchedulerList);
40902
40497
  setVisible("topbar-scheduler-refresh-btn", isSchedulerList);
@@ -41148,7 +40743,12 @@ Waiting for Claude login output…</pre
41148
40743
 
41149
40744
  function applyNavigationState() {
41150
40745
  const screen = getCurrentScreen();
41151
- const collectionSurface = ["sessions", "web", "mail"].includes(screen)
40746
+ const collectionSurface = [
40747
+ "sessions",
40748
+ "folder-sessions",
40749
+ "web",
40750
+ "mail",
40751
+ ].includes(screen)
41152
40752
  ? screen
41153
40753
  : "";
41154
40754
  recordHostTelemetryFeatureView(getCurrentRoute());
@@ -41156,6 +40756,10 @@ Waiting for Claude login output…</pre
41156
40756
  closeSessionLoopActionMenu();
41157
40757
  closeHostSchedulerActionMenu();
41158
40758
  $("entry-view").classList.toggle("active", screen === "sessions");
40759
+ $("folder-sessions-view").classList.toggle(
40760
+ "active",
40761
+ screen === "folder-sessions"
40762
+ );
41159
40763
  $("web-view").classList.toggle("active", screen === "web");
41160
40764
  $("mail-view").classList.toggle("active", screen === "mail");
41161
40765
  $("dashboard-workspace")?.classList.toggle(
@@ -41207,6 +40811,14 @@ Waiting for Claude login output…</pre
41207
40811
  screen === "tool-detail"
41208
40812
  );
41209
40813
  $("browser-view").classList.toggle("active", screen === "browser");
40814
+ // The shared row menu layer is outside every page; close it when its
40815
+ // row is no longer on the visible page.
40816
+ if (
40817
+ sessionActionMenuState &&
40818
+ !sessionActionMenuState.anchorButton?.closest?.(".view.active")
40819
+ ) {
40820
+ closeSessionActionMenu();
40821
+ }
41210
40822
  if (screen === "explorer") {
41211
40823
  renderExplorerContextChrome();
41212
40824
  }
@@ -41395,6 +41007,14 @@ Waiting for Claude login output…</pre
41395
41007
 
41396
41008
  function handleTopbarBack() {
41397
41009
  const currentBackRoute = getCurrentRoute();
41010
+ if (currentBackRoute.screen === "folder-sessions") {
41011
+ // Explorer is a separate document; return to the exact page that
41012
+ // opened Resume Session, as the folder picker does.
41013
+ window.location.assign(
41014
+ getFolderSessionsReturnTarget(currentBackRoute)
41015
+ );
41016
+ return;
41017
+ }
41398
41018
  if (navigatePreviousPageRouteMapFallback(currentBackRoute)) {
41399
41019
  return;
41400
41020
  }
@@ -41437,7 +41057,6 @@ Waiting for Claude login output…</pre
41437
41057
  "browse-context-menu",
41438
41058
  "picker-modal-backdrop",
41439
41059
  "browse-create-folder-backdrop",
41440
- "agent-store-demo-backdrop",
41441
41060
  "provider-auth-backdrop",
41442
41061
  "chat-image-preview-backdrop",
41443
41062
  "command-palette",
@@ -42079,7 +41698,7 @@ Waiting for Claude login output…</pre
42079
41698
  $("model-hint").textContent =
42080
41699
  "Spillover Fake deterministic count model for P82 verification.";
42081
41700
  $("runtime-hint").textContent =
42082
- "Uses the hidden P82 Spillover Fake provider. It is available only when Host config.debug.json enables large tool spillover verification.";
41701
+ "Uses the hidden P82 Spillover Fake provider. It is available only when Host config/debug.json enables large tool spillover verification.";
42083
41702
  } else if (createRuntime === "debug-p135-codex-replay") {
42084
41703
  modelSelect.disabled = modelSelection.options.length === 0;
42085
41704
  if (claudeReasoningEffortSelect) {
@@ -42091,7 +41710,7 @@ Waiting for Claude login output…</pre
42091
41710
  $("model-hint").textContent =
42092
41711
  "P135 Codex Replay deterministic fixture model.";
42093
41712
  $("runtime-hint").textContent =
42094
- "Uses the hidden P135 Codex Replay provider. It is available only when Host config.debug.json enables replay verification.";
41713
+ "Uses the hidden P135 Codex Replay provider. It is available only when Host config/debug.json enables replay verification.";
42095
41714
  }
42096
41715
 
42097
41716
  if (opts.applyDefaults !== false) {
@@ -42183,6 +41802,8 @@ Waiting for Claude login output…</pre
42183
41802
  showError("login-error", "");
42184
41803
  showLoginNotice("Opening Oysterun...");
42185
41804
  setLoginBusy(true, "Opening…");
41805
+ // Fixed internal account name, never a user input: a P296 Host ignores
41806
+ // it, while an older Host (e.g. after a rolled-back update) requires it.
42186
41807
  const username = "admin";
42187
41808
  const password = $("login-pass").value;
42188
41809
  try {
@@ -43397,6 +43018,7 @@ Waiting for Claude login output…</pre
43397
43018
  session_tags_error: entry.session_tags_error ?? null,
43398
43019
  session_tag_labels_error: entry.session_tag_labels_error ?? null,
43399
43020
  agent_folder: entry.agent_folder,
43021
+ agent_folder_real_path: entry.agent_folder_real_path,
43400
43022
  runtime: entry.runtime,
43401
43023
  model: entry.model,
43402
43024
  created_at: entry.started_at,
@@ -43646,6 +43268,15 @@ Waiting for Claude login output…</pre
43646
43268
  return menuItems.find((item) => item.disabled !== true)?.action || "";
43647
43269
  }
43648
43270
 
43271
+ // Sessions and Resume Session can both hold a History row for the same
43272
+ // Session; prefer the copy in the visible page.
43273
+ function querySessionRowElement(selector) {
43274
+ return (
43275
+ document.querySelector(`.view.active ${selector}`) ||
43276
+ document.querySelector(selector)
43277
+ );
43278
+ }
43279
+
43649
43280
  function closeSessionActionMenu(opts = {}) {
43650
43281
  const layer = $("session-action-menu-layer");
43651
43282
  if (layer) {
@@ -43655,7 +43286,7 @@ Waiting for Claude login output…</pre
43655
43286
  const closingState = sessionActionMenuState;
43656
43287
  closingState?.expandedButton?.setAttribute?.("aria-expanded", "false");
43657
43288
  const currentExpandedButton = closingState?.sessionId
43658
- ? document.querySelector(
43289
+ ? querySessionRowElement(
43659
43290
  `[data-session-action-menu-session-id="${CSS.escape(
43660
43291
  closingState.sessionId
43661
43292
  )}"]`
@@ -43674,7 +43305,7 @@ Waiting for Claude login output…</pre
43674
43305
  closingState.kind === "history"
43675
43306
  ? "data-history-session-id"
43676
43307
  : "data-live-session-id";
43677
- const currentRow = document.querySelector(
43308
+ const currentRow = querySessionRowElement(
43678
43309
  `[${rowAttribute}="${CSS.escape(closingState.sessionId)}"]`
43679
43310
  );
43680
43311
  focusTarget =
@@ -43875,6 +43506,8 @@ Waiting for Claude login output…</pre
43875
43506
  liveSession.agentFolder ||
43876
43507
  liveSession.agent_folder ||
43877
43508
  "",
43509
+ // A running Session has no Host-resolved History folder location.
43510
+ agent_folder_real_path: null,
43878
43511
  runtime: liveSession.provider || liveSession.runtime || null,
43879
43512
  model: liveSession.model || null,
43880
43513
  // Only editable setup values cross this boundary. Native/thread,
@@ -44065,6 +43698,53 @@ Waiting for Claude login output…</pre
44065
43698
  });
44066
43699
  }
44067
43700
 
43701
+ // Shared History Resume eligibility. The Sessions row menu and the
43702
+ // folder Session page buttons use the same pre-check and settle rules.
43703
+ function getHistoryResumePreCheckState(sessionId) {
43704
+ const historyRecord = getHistorySessionRecord(sessionId);
43705
+ const desiredRestore = getDesiredOpenRestoreSession(sessionId);
43706
+ if (historyRecord?.restore_failed !== true) {
43707
+ failedResumeAvailabilityBySession.delete(sessionId);
43708
+ }
43709
+ return desiredRestore &&
43710
+ desiredRestore.state !== "failed" &&
43711
+ desiredRestore.state !== "ready"
43712
+ ? "restoring"
43713
+ : "";
43714
+ }
43715
+
43716
+ function settleHistoryResumeState(sessionId, resumeCheckFailed) {
43717
+ const historyRecord = getHistorySessionRecord(sessionId);
43718
+ let resumeState = resumeCheckFailed
43719
+ ? "retry"
43720
+ : historySessionCanResume(sessionId)
43721
+ ? "supported"
43722
+ : "unavailable";
43723
+ if (historyRecord?.restore_failed === true && !resumeCheckFailed) {
43724
+ const signature = historyResumeAvailabilitySignature(sessionId);
43725
+ const previous = failedResumeAvailabilityBySession.get(sessionId);
43726
+ const availabilityChanged =
43727
+ previous && previous.signature !== signature;
43728
+ const unlocked = Boolean(previous?.unlocked || availabilityChanged);
43729
+ failedResumeAvailabilityBySession.set(sessionId, {
43730
+ signature,
43731
+ unlocked,
43732
+ });
43733
+ // A failed provider Resume stays disabled on this check. A later
43734
+ // check may offer Resume Chat only after availability changes and
43735
+ // the full transcript/provider eligibility check passes.
43736
+ if (
43737
+ !unlocked &&
43738
+ !isDeterministicRestoreUnavailableReason(
43739
+ historyRecord.restore_failed_reason
43740
+ )
43741
+ ) {
43742
+ resumeState = "unavailable";
43743
+ }
43744
+ }
43745
+ return resumeState;
43746
+ }
43747
+
44068
43748
  function canSettleSessionActionMenu(openedState, currentState, signal) {
44069
43749
  return currentState === openedState && signal?.aborted !== true;
44070
43750
  }
@@ -44275,15 +43955,8 @@ Waiting for Claude login output…</pre
44275
43955
  installSessionActionMenuEnvironmentalDismissHandlers();
44276
43956
  if (normalizedKind === "history") {
44277
43957
  const historyRecord = getHistorySessionRecord(normalizedSessionId);
44278
- const desiredRestore =
44279
- getDesiredOpenRestoreSession(normalizedSessionId);
44280
- if (historyRecord?.restore_failed !== true) {
44281
- failedResumeAvailabilityBySession.delete(normalizedSessionId);
44282
- }
44283
43958
  if (
44284
- desiredRestore &&
44285
- desiredRestore.state !== "failed" &&
44286
- desiredRestore.state !== "ready"
43959
+ getHistoryResumePreCheckState(normalizedSessionId) === "restoring"
44287
43960
  ) {
44288
43961
  renderOpenSessionActionMenu(
44289
43962
  anchorButton,
@@ -44361,35 +44034,10 @@ Waiting for Claude login output…</pre
44361
44034
  ) {
44362
44035
  return;
44363
44036
  }
44364
- let resumeState = resumeCheckFailed
44365
- ? "retry"
44366
- : historySessionCanResume(normalizedSessionId)
44367
- ? "supported"
44368
- : "unavailable";
44369
- if (historyRecord?.restore_failed === true && !resumeCheckFailed) {
44370
- const signature =
44371
- historyResumeAvailabilitySignature(normalizedSessionId);
44372
- const previous =
44373
- failedResumeAvailabilityBySession.get(normalizedSessionId);
44374
- const availabilityChanged =
44375
- previous && previous.signature !== signature;
44376
- const unlocked = Boolean(previous?.unlocked || availabilityChanged);
44377
- failedResumeAvailabilityBySession.set(normalizedSessionId, {
44378
- signature,
44379
- unlocked,
44380
- });
44381
- // A failed provider Resume stays disabled on this menu opening.
44382
- // A later opening may offer Resume Chat only after availability
44383
- // changes and the full transcript/provider eligibility check passes.
44384
- if (
44385
- !unlocked &&
44386
- !isDeterministicRestoreUnavailableReason(
44387
- historyRecord.restore_failed_reason
44388
- )
44389
- ) {
44390
- resumeState = "unavailable";
44391
- }
44392
- }
44037
+ const resumeState = settleHistoryResumeState(
44038
+ normalizedSessionId,
44039
+ resumeCheckFailed
44040
+ );
44393
44041
  openedMenuState.resumeState = resumeState;
44394
44042
  renderOpenSessionActionMenu(
44395
44043
  anchorButton,
@@ -44662,7 +44310,11 @@ Waiting for Claude login output…</pre
44662
44310
  `;
44663
44311
  }
44664
44312
 
44665
- function renderHistorySessionRow(record) {
44313
+ // `inlineActions` (folder Session page) replaces the row-menu trigger
44314
+ // with a non-interactive row body plus the given inline action buttons.
44315
+ function renderHistorySessionRow(record, opts = {}) {
44316
+ const inlineActions =
44317
+ typeof opts.inlineActions === "string" ? opts.inlineActions : null;
44666
44318
  const runtime = record.runtime || "claude";
44667
44319
  const model = record.model || "default model";
44668
44320
  const displayName = getHistorySessionDisplayName(record);
@@ -44681,30 +44333,7 @@ Waiting for Claude login output…</pre
44681
44333
  const expanded =
44682
44334
  sessionActionMenuState?.sessionId === record.session_id &&
44683
44335
  sessionActionMenuState?.kind === "history";
44684
- return `
44685
- <article
44686
- class="collection-row session-list-row session-list-row-history"
44687
- data-history-session-id="${esc(record.session_id)}"
44688
- data-agent-id="${esc(record.agent_id)}"
44689
- data-session-preview-status="${esc(
44690
- previewState?.status || "loading"
44691
- )}"
44692
- data-completed-session-unread="${unread ? "true" : "false"}"
44693
- >
44694
- <button
44695
- class="collection-row-main session-row-main"
44696
- type="button"
44697
- data-session-action-menu-session-id="${esc(record.session_id)}"
44698
- data-session-action-menu-kind="history"
44699
- aria-haspopup="menu"
44700
- aria-expanded="${String(expanded)}"
44701
- aria-label="Session actions for ${esc(displayName)}${
44702
- unread ? ", unread completed message" : ""
44703
- }"
44704
- data-session-base-aria-label="Session actions for ${esc(
44705
- displayName
44706
- )}"
44707
- >
44336
+ const rowBody = `
44708
44337
  ${renderSessionUnreadSlot(unread)}
44709
44338
  ${renderSessionAvatar(runtime, false)}
44710
44339
  <span class="collection-row-copy session-row-copy">
@@ -44728,7 +44357,38 @@ Waiting for Claude login output…</pre
44728
44357
  </span>
44729
44358
  ${renderSessionTagDiagnostic(record)}
44730
44359
  </span>
44731
- </button>
44360
+ `;
44361
+ return `
44362
+ <article
44363
+ class="collection-row session-list-row session-list-row-history${
44364
+ inlineActions === null ? "" : " session-list-row-inline-actions"
44365
+ }"
44366
+ data-history-session-id="${esc(record.session_id)}"
44367
+ data-agent-id="${esc(record.agent_id)}"
44368
+ data-session-preview-status="${esc(
44369
+ previewState?.status || "loading"
44370
+ )}"
44371
+ data-completed-session-unread="${unread ? "true" : "false"}"
44372
+ >
44373
+ ${
44374
+ inlineActions === null
44375
+ ? `<button
44376
+ class="collection-row-main session-row-main"
44377
+ type="button"
44378
+ data-session-action-menu-session-id="${esc(record.session_id)}"
44379
+ data-session-action-menu-kind="history"
44380
+ aria-haspopup="menu"
44381
+ aria-expanded="${String(expanded)}"
44382
+ aria-label="Session actions for ${esc(displayName)}${
44383
+ unread ? ", unread completed message" : ""
44384
+ }"
44385
+ data-session-base-aria-label="Session actions for ${esc(
44386
+ displayName
44387
+ )}"
44388
+ >${rowBody}</button>`
44389
+ : `<div class="collection-row-main session-row-main">${rowBody}</div>
44390
+ <div class="session-row-inline-actions">${inlineActions}</div>`
44391
+ }
44732
44392
  </article>
44733
44393
  `;
44734
44394
  }
@@ -44797,7 +44457,262 @@ Waiting for Claude login output…</pre
44797
44457
  </section>`;
44798
44458
  }
44799
44459
 
44460
+ // Resume Session compares real filesystem locations: the chosen folder
44461
+ // is resolved by the Host and each History row carries the Host-resolved
44462
+ // agent_folder_real_path, so path spelling, aliases and symlinks match.
44463
+ let folderSessionsFolderResolution = {
44464
+ folder: "",
44465
+ realPath: "",
44466
+ error: "",
44467
+ };
44468
+
44469
+ function resolveFolderSessionsFolder(folder) {
44470
+ if (folderSessionsFolderResolution.folder === folder) {
44471
+ return folderSessionsFolderResolution;
44472
+ }
44473
+ const resolution = { folder, realPath: "", error: "" };
44474
+ folderSessionsFolderResolution = resolution;
44475
+ apiWithAuthRetry("POST", "/dev/folders/validate", { path: folder })
44476
+ .then((result) => {
44477
+ const realPath = sanitizeRouteText(result?.path);
44478
+ if (!realPath) {
44479
+ throw new Error("Folder validation did not return a real path.");
44480
+ }
44481
+ resolution.realPath = realPath;
44482
+ })
44483
+ .catch((err) => {
44484
+ resolution.error = err?.message || String(err);
44485
+ showBanner(
44486
+ `Could not resolve this folder: ${resolution.error}`,
44487
+ true
44488
+ );
44489
+ })
44490
+ .finally(() => {
44491
+ if (folderSessionsFolderResolution === resolution) {
44492
+ renderFolderSessionsView();
44493
+ }
44494
+ });
44495
+ return resolution;
44496
+ }
44497
+
44498
+ function getFolderSessionsHistoryRecords(folderRealPath) {
44499
+ const liveSessionIds = new Set(
44500
+ liveSessions.map((entry) => entry.sessionId)
44501
+ );
44502
+ return sessionHistory.filter((record) => {
44503
+ if (liveSessionIds.has(record.session_id)) return false;
44504
+ if (record.agent_folder_real_path === undefined) {
44505
+ throw new Error(
44506
+ `History Session ${record.session_id} is missing agent_folder_real_path.`
44507
+ );
44508
+ }
44509
+ return record.agent_folder_real_path === folderRealPath;
44510
+ });
44511
+ }
44512
+
44513
+ // Resume button state per Session on the folder Session page: one forced
44514
+ // eligibility read per agent, settled with the shared History rules.
44515
+ let folderSessionsResumeStates = new Map();
44516
+
44517
+ function checkFolderSessionsResume(records, opts = {}) {
44518
+ const pending = records.filter(
44519
+ (record) =>
44520
+ opts.force === true ||
44521
+ !folderSessionsResumeStates.has(record.session_id)
44522
+ );
44523
+ const checksByAgent = new Map();
44524
+ for (const record of pending) {
44525
+ if (
44526
+ getHistoryResumePreCheckState(record.session_id) === "restoring"
44527
+ ) {
44528
+ folderSessionsResumeStates.set(record.session_id, {
44529
+ resumeState: "restoring",
44530
+ });
44531
+ continue;
44532
+ }
44533
+ folderSessionsResumeStates.set(record.session_id, {
44534
+ resumeState: "checking",
44535
+ });
44536
+ const group = checksByAgent.get(record.agent_id) || [];
44537
+ group.push(record.session_id);
44538
+ checksByAgent.set(record.agent_id, group);
44539
+ }
44540
+ for (const sessionIds of checksByAgent.values()) {
44541
+ const checkingPresentation =
44542
+ waitForHistoryResumeCheckingPresentation();
44543
+ void validateHistorySessionResumeMembership(sessionIds[0], {
44544
+ force: true,
44545
+ })
44546
+ .then(
44547
+ () => ({ failed: false, timedOut: false }),
44548
+ (err) => {
44549
+ console.warn(
44550
+ "[oysterun-ui] Could not check folder Session Resume",
44551
+ err
44552
+ );
44553
+ return {
44554
+ failed: true,
44555
+ timedOut: err?.code === "history_resume_check_timeout",
44556
+ };
44557
+ }
44558
+ )
44559
+ .then(async (result) => {
44560
+ await checkingPresentation;
44561
+ for (const sessionId of sessionIds) {
44562
+ if (
44563
+ folderSessionsResumeStates.get(sessionId)?.resumeState !==
44564
+ "checking"
44565
+ )
44566
+ continue;
44567
+ const resumeState = settleHistoryResumeState(
44568
+ sessionId,
44569
+ result.failed
44570
+ );
44571
+ folderSessionsResumeStates.set(sessionId, {
44572
+ resumeState,
44573
+ resumeCheckTimedOut: result.timedOut,
44574
+ resumeUnavailableReason:
44575
+ resumeState === "unavailable"
44576
+ ? historySessionResumeMenuUnavailableReason(sessionId)
44577
+ : "",
44578
+ });
44579
+ }
44580
+ renderFolderSessionsView();
44581
+ });
44582
+ }
44583
+ }
44584
+
44585
+ function renderFolderSessionsActionButton(item) {
44586
+ const accessibleLabel = [item.label, item.reason]
44587
+ .filter(Boolean)
44588
+ .join(". ");
44589
+ const disabled = item.disabled === true;
44590
+ return `
44591
+ <button
44592
+ class="session-row-inline-action"
44593
+ type="button"
44594
+ data-folder-sessions-action="${esc(item.action)}"
44595
+ ${
44596
+ item.resumeState
44597
+ ? `data-history-resume-state="${esc(item.resumeState)}"`
44598
+ : ""
44599
+ }
44600
+ aria-label="${esc(accessibleLabel)}"
44601
+ title="${esc(accessibleLabel)}"
44602
+ aria-disabled="${String(disabled)}"
44603
+ ${disabled ? "disabled" : ""}
44604
+ >
44605
+ <span class="session-row-menu-action-icon">${renderSessionActionIcon(
44606
+ item.icon
44607
+ )}</span>
44608
+ </button>
44609
+ `;
44610
+ }
44611
+
44612
+ // Inline Resume and Duplicate Setup use the History menu items, so labels,
44613
+ // enablement and unavailable reasons match the Sessions row menu.
44614
+ function renderFolderSessionsRowActions(record) {
44615
+ const state = folderSessionsResumeStates.get(record.session_id) || {
44616
+ resumeState: "checking",
44617
+ };
44618
+ const items = getSessionActionMenuItems("history", record.session_id, {
44619
+ resumeState: state.resumeState,
44620
+ resumeCheckTimedOut: state.resumeCheckTimedOut === true,
44621
+ resumeUnavailableReason: state.resumeUnavailableReason || "",
44622
+ });
44623
+ const resumeItem = items.find((item) => item.resumeState);
44624
+ const duplicateItem = items.find((item) => item.action === "duplicate");
44625
+ if (!resumeItem || !duplicateItem) {
44626
+ throw new Error(
44627
+ "History actions must include Resume and Duplicate Setup."
44628
+ );
44629
+ }
44630
+ return [resumeItem, duplicateItem]
44631
+ .map(renderFolderSessionsActionButton)
44632
+ .join("");
44633
+ }
44634
+
44635
+ function wireFolderSessionsView(container, folder) {
44636
+ container
44637
+ .querySelector("[data-folder-sessions-new-session]")
44638
+ ?.addEventListener("click", () => {
44639
+ void navigateToRoute(buildSessionSetupRoute({ folder }));
44640
+ });
44641
+ for (const button of container.querySelectorAll(
44642
+ "[data-folder-sessions-action]"
44643
+ )) {
44644
+ button.addEventListener("click", () => {
44645
+ const sessionId =
44646
+ button.closest("[data-history-session-id]")?.dataset
44647
+ .historySessionId || "";
44648
+ const action = button.dataset.folderSessionsAction;
44649
+ if (!sessionId) return;
44650
+ if (action === "resume") {
44651
+ void openHistorySession(sessionId, { resume: true });
44652
+ } else if (action === "duplicate") {
44653
+ void openHistorySession(sessionId, { resume: false });
44654
+ } else if (action === "retry_resume_check") {
44655
+ // Retries only the availability read, never provider Resume.
44656
+ const record = getHistorySessionRecord(sessionId);
44657
+ if (record) checkFolderSessionsResume([record], { force: true });
44658
+ renderFolderSessionsView();
44659
+ }
44660
+ });
44661
+ }
44662
+ }
44663
+
44664
+ function renderFolderSessionsView() {
44665
+ const route = normalizeClientRoute(getCurrentRoute());
44666
+ const container = $("folder-sessions-list");
44667
+ if (route.screen !== "folder-sessions" || !container) return;
44668
+ const resolution = resolveFolderSessionsFolder(route.folder);
44669
+ const historyLoaded = Boolean(dashboardAppliedCollectionIdentity);
44670
+ const ready = historyLoaded && Boolean(resolution.realPath);
44671
+ const records = ready
44672
+ ? getFolderSessionsHistoryRecords(resolution.realPath)
44673
+ : [];
44674
+ if (records.length) checkFolderSessionsResume(records);
44675
+ const historyRows = records
44676
+ .map((record) =>
44677
+ renderHistorySessionRow(record, {
44678
+ inlineActions: renderFolderSessionsRowActions(record),
44679
+ })
44680
+ )
44681
+ .join("");
44682
+ container.dataset.folderSessionsFolder = route.folder;
44683
+ container.dataset.folderSessionsFolderRealPath = resolution.realPath;
44684
+ container.innerHTML = `
44685
+ <div class="folder-sessions-intro">
44686
+ <div class="surface-action-row">
44687
+ <button
44688
+ class="btn-primary"
44689
+ type="button"
44690
+ data-folder-sessions-new-session="true"
44691
+ data-oysterun-normal-session-start-action="true"
44692
+ >+ New Session</button>
44693
+ </div>
44694
+ ${
44695
+ historyRows
44696
+ ? '<div class="meta-text folder-sessions-label">Previous Sessions</div>'
44697
+ : ""
44698
+ }
44699
+ </div>
44700
+ ${
44701
+ historyRows
44702
+ ? `<div class="session-section-rows">${historyRows}</div>`
44703
+ : ready
44704
+ ? `<div class="empty-state sessions-empty-state">
44705
+ <div class="sessions-empty-icon" aria-hidden="true"></div>
44706
+ <strong>No previous Sessions in this folder.</strong>
44707
+ </div>`
44708
+ : ""
44709
+ }
44710
+ `;
44711
+ wireFolderSessionsView(container, route.folder);
44712
+ }
44713
+
44800
44714
  function renderCatalog() {
44715
+ renderFolderSessionsView();
44801
44716
  const entryContainer = $("entry-agent-list");
44802
44717
  const focusedSessionTagGroupIdentity =
44803
44718
  document.activeElement?.dataset?.sessionTagGroupToggle || "";
@@ -46367,8 +46282,7 @@ Waiting for Claude login output…</pre
46367
46282
  }
46368
46283
  }
46369
46284
 
46370
- function wireEntryCardEvents() {
46371
- const entryContainer = $("entry-agent-list");
46285
+ function wireEntryCardEvents(entryContainer = $("entry-agent-list")) {
46372
46286
  if (!entryContainer) return;
46373
46287
 
46374
46288
  for (const toggle of entryContainer.querySelectorAll(
@@ -47046,7 +46960,6 @@ Waiting for Claude login output…</pre
47046
46960
  : HOST_DEFAULT_SESSION_DEFAULTS.xai.approvalPolicy;
47047
46961
  const savedDraft = buildHostPreferencesDraft({
47048
46962
  displayName: data.display_name || "",
47049
- publicBaseUrl: data.direct_host_url || data.public_base_url || "",
47050
46963
  defaultBrowsePath: data.default_browse_path || "",
47051
46964
  notificationSoundWebEnabled,
47052
46965
  notificationSoundAppEnabled,
@@ -47091,7 +47004,6 @@ Waiting for Claude login output…</pre
47091
47004
  loaded: true,
47092
47005
  loading: false,
47093
47006
  saving: false,
47094
- connectionMode: data.connection_mode === "cloud" ? "cloud" : "direct",
47095
47007
  platform: typeof data.platform === "string" ? data.platform : "",
47096
47008
  folderAccessSettingsSupported:
47097
47009
  data.folder_access_settings_supported === true,
@@ -47165,8 +47077,6 @@ Waiting for Claude login output…</pre
47165
47077
  allowedPathDisabled,
47166
47078
  allowedPathDebugUiEnabled,
47167
47079
  providerPermissionDebugModeDropdownEnabled,
47168
- publicBaseUrl: data.direct_host_url || data.public_base_url || "",
47169
- activeDirectHostUrl: data.active_direct_host_url || "",
47170
47080
  managedTunnelEnabled: data.managed_tunnel_enabled === true,
47171
47081
  managedTunnelStatus:
47172
47082
  data.managed_tunnel_status === "ready" ||
@@ -47202,8 +47112,6 @@ Waiting for Claude login output…</pre
47202
47112
  hostPreferencesState.notificationServiceWorkerState || "unknown",
47203
47113
  notificationLastError:
47204
47114
  hostPreferencesState.notificationLastError || "",
47205
- directHostUrlRestartRequired:
47206
- data.direct_host_url_restart_required === true,
47207
47115
  hostRestartSupported: data.host_restart_supported === true,
47208
47116
  loginQr: hostPreferencesState.loginQr,
47209
47117
  loginQrLoading: false,
@@ -49844,10 +49752,6 @@ Waiting for Claude login output…</pre
49844
49752
  if (displayNameInput && document.activeElement !== displayNameInput) {
49845
49753
  displayNameInput.value = hostPreferencesState.displayName || "";
49846
49754
  }
49847
- const publicUrlInput = $("host-public-base-url");
49848
- if (publicUrlInput && document.activeElement !== publicUrlInput) {
49849
- publicUrlInput.value = hostPreferencesState.publicBaseUrl || "";
49850
- }
49851
49755
  if ($("host-default-provider")) {
49852
49756
  renderHostDefaultProviderOptions();
49853
49757
  }
@@ -49958,11 +49862,6 @@ Waiting for Claude login output…</pre
49958
49862
  "disabled",
49959
49863
  providerCatalogState.loading
49960
49864
  );
49961
- const loginQrUsesManagedEndpoint =
49962
- hostPreferencesState.connectionMode === "cloud";
49963
- const directAccessHint = loginQrUsesManagedEndpoint
49964
- ? "Managed login QR uses the healthy Oysterun Managed Endpoint. Direct Host URL remains available for manual Direct access."
49965
- : "Used by app login QR and manual iPhone connection. Save changes before generating QR.";
49966
49865
  const localUrlInput = $("host-preferences-local-url");
49967
49866
  if (localUrlInput) {
49968
49867
  localUrlInput.value = hostPreferencesState.localUrl || "";
@@ -49999,10 +49898,10 @@ Waiting for Claude login output…</pre
49999
49898
  "host-managed-tunnel-status-hint",
50000
49899
  managedTunnelStatus === "denied"
50001
49900
  ? hostPreferencesState.managedTunnelStatusDetail ||
50002
- "Managed registration was denied. Direct Host access remains available."
49901
+ "Managed registration was denied. This Host's Tunnel URL is not reachable until registration succeeds."
50003
49902
  : managedTunnelStatus === "pending"
50004
49903
  ? hostPreferencesState.managedTunnelStatusDetail ||
50005
- "Managed Connector is still retrying. Direct Host access remains available."
49904
+ "Managed Connector is still retrying. The Host service runs locally, but its Tunnel URL is not reachable yet."
50006
49905
  : ""
50007
49906
  );
50008
49907
  const managedQuota = hostPreferencesState.managedTunnelQuota;
@@ -50177,49 +50076,21 @@ Waiting for Claude login output…</pre
50177
50076
  "disabled",
50178
50077
  !managedDiagnostics
50179
50078
  );
50180
- setText("host-public-base-url-hint", directAccessHint);
50181
- const directUrlCopyButton = $("host-public-base-url-copy-btn");
50182
- if (directUrlCopyButton) {
50183
- directUrlCopyButton.disabled = !String(
50184
- publicUrlInput?.value || hostPreferencesState.publicBaseUrl || ""
50185
- ).trim();
50186
- }
50187
- const hasDirectAccessDraftChanges = hasUnsavedHostPreferencesChanges();
50188
- const savedDirectHostUrl = hostPreferencesState.publicBaseUrl || "";
50189
- const activeDirectHostUrl =
50190
- hostPreferencesState.activeDirectHostUrl || savedDirectHostUrl;
50079
+ // Every login QR carries this Host's formal URL: its registered Tunnel URL.
50191
50080
  const managedLoginQrReady =
50192
50081
  hostPreferencesState.managedTunnelEnabled &&
50193
50082
  managedTunnelStatus === "ready" &&
50194
50083
  Boolean(hostPreferencesState.managedEndpoint);
50195
- const loginQrBlockedByDraftChanges =
50196
- !loginQrUsesManagedEndpoint && hasDirectAccessDraftChanges;
50197
- const loginQrTargetReady = loginQrUsesManagedEndpoint
50198
- ? managedLoginQrReady
50199
- : Boolean(savedDirectHostUrl);
50084
+ const hasDirectAccessDraftChanges = hasUnsavedHostPreferencesChanges();
50085
+ const loginQrTargetReady = managedLoginQrReady;
50200
50086
  let directAccessStatus = "";
50201
50087
  if (hostPreferencesState.loginQrError) {
50202
50088
  directAccessStatus = hostPreferencesState.loginQrError;
50203
- } else if (loginQrUsesManagedEndpoint) {
50089
+ } else {
50204
50090
  directAccessStatus = managedLoginQrReady
50205
50091
  ? `QR will use Oysterun Managed Endpoint ${hostPreferencesState.managedEndpoint}.`
50206
50092
  : hostPreferencesState.managedTunnelStatusDetail ||
50207
50093
  "Managed Endpoint must be Ready before generating a login QR.";
50208
- } else if (hostPreferencesState.directHostUrlRestartRequired) {
50209
- directAccessStatus =
50210
- "Saved URL differs from the running Host URL. Restart Host before using the new app connection URL.";
50211
- } else if (savedDirectHostUrl) {
50212
- directAccessStatus = `QR will use ${savedDirectHostUrl}.`;
50213
- } else {
50214
- directAccessStatus =
50215
- "Set a Direct Host URL before generating a login QR.";
50216
- }
50217
- if (
50218
- activeDirectHostUrl &&
50219
- savedDirectHostUrl &&
50220
- activeDirectHostUrl !== savedDirectHostUrl
50221
- ) {
50222
- directAccessStatus += ` Running URL: ${activeDirectHostUrl}.`;
50223
50094
  }
50224
50095
  setText("host-direct-access-status", directAccessStatus);
50225
50096
  const restartHandoff = hostPreferencesState.restartHandoff;
@@ -50247,7 +50118,6 @@ Waiting for Claude login output…</pre
50247
50118
  qrButton.disabled =
50248
50119
  disabled ||
50249
50120
  hostPreferencesState.loginQrLoading ||
50250
- loginQrBlockedByDraftChanges ||
50251
50121
  !loginQrTargetReady;
50252
50122
  qrButton.textContent = hostPreferencesState.loginQrLoading
50253
50123
  ? "Generating..."
@@ -50255,9 +50125,11 @@ Waiting for Claude login output…</pre
50255
50125
  }
50256
50126
  const restartButton = $("host-restart-btn");
50257
50127
  if (restartButton) {
50258
- const restartBlockedByUpdate = isOysterunUpdateOperationBlocking(
50259
- hostPreferencesState.update
50260
- );
50128
+ // An unfinished restart continues through Restart Host itself.
50129
+ const restartBlockedByUpdate =
50130
+ isOysterunUpdateOperationBlocking(hostPreferencesState.update) &&
50131
+ getHostLifecycleRecoveryUserAction(hostPreferencesState.update)
50132
+ ?.kind !== "restart_only";
50261
50133
  restartButton.disabled =
50262
50134
  disabled ||
50263
50135
  hostPreferencesState.restartLoading ||
@@ -50336,10 +50208,18 @@ Waiting for Claude login output…</pre
50336
50208
  betaUpdateVisible && hostPreferencesState.betaUpdateRefreshing
50337
50209
  );
50338
50210
  const updatePhase = getOysterunUpdatePhase(update);
50211
+ const restartRecoveryDescription =
50212
+ getHostLifecycleRecoveryUserAction(update)?.kind === "restart_only"
50213
+ ? getHostLifecycleRecoveryUserAction(update).description || ""
50214
+ : "";
50339
50215
  setText(
50340
50216
  "host-restart-status",
50341
50217
  hostPreferencesState.restartStatus ||
50342
- (restartOperation ? updatePhase : "")
50218
+ (restartOperation
50219
+ ? [updatePhase, restartRecoveryDescription]
50220
+ .filter(Boolean)
50221
+ .join(" ")
50222
+ : "")
50343
50223
  );
50344
50224
  setText("host-update-phase", restartOperation ? "" : updatePhase);
50345
50225
  setText(
@@ -50358,7 +50238,16 @@ Waiting for Claude login output…</pre
50358
50238
  typeof recoveryPlan.user_action === "object"
50359
50239
  ? recoveryPlan.user_action
50360
50240
  : null;
50361
- const updateRecoveryRequired = Boolean(recoveryAction?.kind);
50241
+ // Restart and update continuations use Restart Host / Update & Restart;
50242
+ // only Restore and the unverifiable-data diagnostics keep this entry.
50243
+ const updateEntryRecovery = [
50244
+ "complete_update_and_restart",
50245
+ "repair_and_restart",
50246
+ ].includes(recoveryAction?.kind);
50247
+ const updateRecoveryRequired = [
50248
+ "restore_and_restart",
50249
+ "export_diagnostics",
50250
+ ].includes(recoveryAction?.kind);
50362
50251
  const recoveryButton = $("host-update-recovery-btn");
50363
50252
  if (recoveryButton) {
50364
50253
  recoveryButton.classList.toggle("hidden", !updateRecoveryRequired);
@@ -50371,17 +50260,35 @@ Waiting for Claude login output…</pre
50371
50260
  ? "Working..."
50372
50261
  : recoveryAction?.label || "Download diagnostic summary";
50373
50262
  }
50263
+ // P298 (Owner decision A): a recovery whose card button opens a
50264
+ // confirmation also offers the redacted summary on the card, before it.
50265
+ const cardDiagnosticsButton = $("host-update-card-diagnostics-btn");
50266
+ if (cardDiagnosticsButton) {
50267
+ cardDiagnosticsButton.classList.toggle(
50268
+ "hidden",
50269
+ recoveryAction?.kind !== "restore_and_restart" ||
50270
+ updateOperationRunning
50271
+ );
50272
+ cardDiagnosticsButton.disabled =
50273
+ disabled || hostPreferencesState.updateRecoveryChecking;
50274
+ cardDiagnosticsButton.textContent =
50275
+ hostPreferencesState.updateRecoveryChecking
50276
+ ? "Preparing summary..."
50277
+ : "Download diagnostic summary";
50278
+ }
50279
+ const updateRecoveryDescription =
50280
+ recoveryAction?.kind === "restart_only"
50281
+ ? ""
50282
+ : recoveryAction?.description || "";
50374
50283
  setText(
50375
50284
  "host-update-recovery-status",
50376
- hostPreferencesState.updateRecoveryStatus ||
50377
- recoveryAction?.description ||
50378
- ""
50285
+ hostPreferencesState.updateRecoveryStatus || updateRecoveryDescription
50379
50286
  );
50380
50287
  setVisible(
50381
50288
  "host-update-recovery-status",
50382
50289
  Boolean(
50383
50290
  hostPreferencesState.updateRecoveryStatus ||
50384
- recoveryAction?.description
50291
+ updateRecoveryDescription
50385
50292
  )
50386
50293
  );
50387
50294
  const updateCanRun =
@@ -50485,13 +50392,16 @@ Waiting for Claude login output…</pre
50485
50392
  }
50486
50393
  const updateRunButton = $("host-update-run-btn");
50487
50394
  if (updateRunButton) {
50395
+ // A prepared or unfinished install continues its original operation
50396
+ // through Update & Restart; otherwise this starts a new update.
50488
50397
  updateRunButton.disabled =
50489
50398
  disabled ||
50490
50399
  hostPreferencesState.updateChecking ||
50491
50400
  hostPreferencesState.updateRunning ||
50492
- updateOperationBlocking ||
50493
50401
  updateConfirmationVisible ||
50494
- !updateCanRun;
50402
+ (updateEntryRecovery
50403
+ ? updateOperationRunning
50404
+ : updateOperationBlocking || !updateCanRun);
50495
50405
  updateRunButton.textContent = hostPreferencesState.updateRunning
50496
50406
  ? "Installing..."
50497
50407
  : "Update & Restart";
@@ -50537,6 +50447,12 @@ Waiting for Claude login output…</pre
50537
50447
  ? "Confirm beta install and restart"
50538
50448
  : "Confirm update and restart"
50539
50449
  );
50450
+ setText(
50451
+ "host-update-confirmation-warning",
50452
+ recoveryConfirmation
50453
+ ? "Oysterun continues this operation with its retained Session restore state; a new one is prepared only if the operation stopped before its first restart. Loops remain off. Active provider replies, terminal commands, loop turns, and scheduler work may be marked interrupted and will not be replayed."
50454
+ : "Oysterun will prepare restore state for existing Sessions before restarting Host. Loops remain off. Active provider replies, terminal commands, loop turns, and scheduler work may be marked interrupted and will not be replayed."
50455
+ );
50540
50456
  setText("host-update-confirmation-current", updateCurrentVersion);
50541
50457
  setText("host-update-confirmation-target", updateTargetVersion);
50542
50458
  setText(
@@ -50582,12 +50498,32 @@ Waiting for Claude login output…</pre
50582
50498
  updateConfirmRunButton.disabled =
50583
50499
  disabled || !updateConfirmationVisible;
50584
50500
  updateConfirmRunButton.textContent = recoveryConfirmation
50585
- ? recoveryAction?.label || "Confirm recovery and restart"
50501
+ ? getHostLifecycleRecoveryPrimaryLabel(updateConfirmationMode)
50586
50502
  : updateConfirmationMode === "beta"
50587
50503
  ? "Confirm Beta Install & Restart"
50588
50504
  : "Confirm Update & Restart";
50589
50505
  setVisible("host-update-confirm-run-btn", updateConfirmationVisible);
50590
50506
  }
50507
+ // Failure/recovery confirmations offer the redacted summary as a
50508
+ // secondary action next to Cancel and the primary operation. Restore
50509
+ // offers it on the card instead (P298, Owner decision X).
50510
+ const updateConfirmDiagnosticsButton = $(
50511
+ "host-update-confirm-diagnostics-btn"
50512
+ );
50513
+ if (updateConfirmDiagnosticsButton) {
50514
+ updateConfirmDiagnosticsButton.disabled =
50515
+ disabled || hostPreferencesState.updateRecoveryChecking;
50516
+ updateConfirmDiagnosticsButton.textContent =
50517
+ hostPreferencesState.updateRecoveryChecking
50518
+ ? "Preparing summary..."
50519
+ : "Download diagnostic summary";
50520
+ setVisible(
50521
+ "host-update-confirm-diagnostics-btn",
50522
+ updateConfirmationVisible &&
50523
+ recoveryConfirmation &&
50524
+ updateConfirmationMode !== "restore_and_restart"
50525
+ );
50526
+ }
50591
50527
  const updateConfirmCancelButton = $("host-update-confirm-cancel-btn");
50592
50528
  if (updateConfirmCancelButton) {
50593
50529
  updateConfirmCancelButton.disabled = disabled;
@@ -50643,7 +50579,6 @@ Waiting for Claude login output…</pre
50643
50579
  showError("host-preferences-error", hostPreferencesState.error);
50644
50580
  const defaultProviderDisabled =
50645
50581
  providerControlsDisabled || getAvailableProviderIds().length === 0;
50646
- const publicUrlDisabled = disabled;
50647
50582
  const folderAccessVisible =
50648
50583
  hostPreferencesState.platform === "darwin" &&
50649
50584
  hostPreferencesState.folderAccessSettingsSupported === true &&
@@ -50698,10 +50633,6 @@ Waiting for Claude login output…</pre
50698
50633
  renderHostNotificationPreferences({ disabled });
50699
50634
  renderHostVoiceInputPreferences({ disabled });
50700
50635
  setVisible("host-folder-access-card", folderAccessVisible);
50701
- $("host-public-base-url")?.toggleAttribute(
50702
- "disabled",
50703
- publicUrlDisabled
50704
- );
50705
50636
  $("host-display-name")?.toggleAttribute("disabled", disabled);
50706
50637
  $("host-default-browse-path")?.toggleAttribute("disabled", disabled);
50707
50638
  $("host-default-browse-path-picker")?.toggleAttribute(
@@ -50829,18 +50760,6 @@ Waiting for Claude login output…</pre
50829
50760
 
50830
50761
  async function generateDirectHostLoginQr() {
50831
50762
  if (hostPreferencesState.loginQrLoading) return;
50832
- if (
50833
- hostPreferencesState.connectionMode !== "cloud" &&
50834
- hasUnsavedHostPreferencesChanges()
50835
- ) {
50836
- hostPreferencesState = {
50837
- ...hostPreferencesState,
50838
- loginQrError:
50839
- "Save Direct Host URL changes before generating a login QR.",
50840
- };
50841
- renderHostPreferences();
50842
- return;
50843
- }
50844
50763
  hostPreferencesState = {
50845
50764
  ...hostPreferencesState,
50846
50765
  loginQrLoading: true,
@@ -50876,6 +50795,19 @@ Waiting for Claude login output…</pre
50876
50795
 
50877
50796
  async function restartHostFromPreferences() {
50878
50797
  if (hostPreferencesState.restartLoading) return;
50798
+ // An unfinished restart continues with its original Session snapshot
50799
+ // after the existing recovery confirmation.
50800
+ const retained = getHostLifecycleRecoveryUserAction(
50801
+ hostPreferencesState.update
50802
+ );
50803
+ if (
50804
+ retained?.kind === "restart_only" &&
50805
+ !hostPreferencesState.loading &&
50806
+ !hostPreferencesState.saving
50807
+ ) {
50808
+ openHostLifecycleRecoveryConfirmation(retained);
50809
+ return;
50810
+ }
50879
50811
  if (
50880
50812
  hostPreferencesState.loading ||
50881
50813
  hostPreferencesState.saving ||
@@ -51081,32 +51013,46 @@ Waiting for Claude login output…</pre
51081
51013
  }
51082
51014
  }
51083
51015
 
51084
- async function runOysterunRecoveryAction() {
51085
- if (hostPreferencesState.updateRecoveryChecking) return;
51016
+ function getHostLifecycleRecoveryUserAction(update) {
51017
+ const action = update?.recovery_plan?.user_action;
51018
+ return action && typeof action === "object" && action.kind
51019
+ ? action
51020
+ : null;
51021
+ }
51022
+
51023
+ function getHostLifecycleRecoveryPrimaryLabel(kind) {
51024
+ // The confirmation keeps the real operation name of its entry.
51025
+ if (kind === "restart_only") return "Restart Host";
51026
+ if (kind === "restore_and_restart") return "Restore and restart";
51027
+ return "Update & Restart";
51028
+ }
51029
+
51030
+ function openHostLifecycleRecoveryConfirmation(action) {
51086
51031
  const update = hostPreferencesState.update || {};
51087
- const action = update.recovery_plan?.user_action || null;
51088
- if (!action?.kind) return;
51089
- if (action.kind !== "export_diagnostics") {
51090
- hostPreferencesState = {
51091
- ...hostPreferencesState,
51092
- updateConfirmationVisible: true,
51093
- updateConfirmationMode: action.kind,
51094
- updateConfirmationCurrentVersion:
51095
- update.current_version || "the current version",
51096
- updateConfirmationTargetVersion:
51097
- action.target_version ||
51098
- update.update_operation?.target_version ||
51099
- update.update_operation?.last_working_version ||
51100
- "the retained version",
51101
- updateRecoveryStatus: action.description || "",
51102
- updateError: "",
51103
- };
51104
- renderHostPreferences();
51105
- window.setTimeout(() => {
51106
- $("host-update-confirm-run-btn")?.focus();
51107
- }, 0);
51108
- return;
51109
- }
51032
+ hostPreferencesState = {
51033
+ ...hostPreferencesState,
51034
+ updateConfirmationVisible: true,
51035
+ updateConfirmationMode: action.kind,
51036
+ updateConfirmationCurrentVersion:
51037
+ update.current_version || "the current version",
51038
+ updateConfirmationTargetVersion:
51039
+ action.target_version ||
51040
+ update.update_operation?.target_version ||
51041
+ update.update_operation?.last_working_version ||
51042
+ "the retained version",
51043
+ updateRecoveryStatus: "",
51044
+ updateError: "",
51045
+ };
51046
+ renderHostPreferences();
51047
+ window.setTimeout(() => {
51048
+ $("host-update-confirm-run-btn")?.focus();
51049
+ }, 0);
51050
+ }
51051
+
51052
+ // Fetches only the redacted summary; never runs install, restore or
51053
+ // restart and leaves any open confirmation as it is.
51054
+ async function downloadHostLifecycleDiagnosticSummary() {
51055
+ if (hostPreferencesState.updateRecoveryChecking) return;
51110
51056
  hostPreferencesState = {
51111
51057
  ...hostPreferencesState,
51112
51058
  updateRecoveryChecking: true,
@@ -51135,6 +51081,7 @@ Waiting for Claude login output…</pre
51135
51081
  "Redacted diagnostic summary downloaded. The current Host was not changed.",
51136
51082
  };
51137
51083
  } catch (error) {
51084
+ if (await handleHostPreferencesAuthFailure(error)) return;
51138
51085
  hostPreferencesState = {
51139
51086
  ...hostPreferencesState,
51140
51087
  updateRecoveryChecking: false,
@@ -51144,12 +51091,42 @@ Waiting for Claude login output…</pre
51144
51091
  renderHostPreferences();
51145
51092
  }
51146
51093
 
51094
+ async function runOysterunRecoveryAction() {
51095
+ if (hostPreferencesState.updateRecoveryChecking) return;
51096
+ const action = getHostLifecycleRecoveryUserAction(
51097
+ hostPreferencesState.update
51098
+ );
51099
+ if (!action) return;
51100
+ if (action.kind === "export_diagnostics") {
51101
+ await downloadHostLifecycleDiagnosticSummary();
51102
+ return;
51103
+ }
51104
+ if (action.kind !== "restore_and_restart") return;
51105
+ openHostLifecycleRecoveryConfirmation(action);
51106
+ }
51107
+
51147
51108
  async function runOysterunUpdate() {
51148
51109
  if (
51149
51110
  hostPreferencesState.updateRunning ||
51150
- hostPreferencesState.updateChecking ||
51151
- isOysterunUpdateOperationBlocking(hostPreferencesState.update)
51111
+ hostPreferencesState.updateChecking
51112
+ ) {
51113
+ return;
51114
+ }
51115
+ // A prepared update or unfinished install continues its original
51116
+ // operation and exact version instead of selecting a new release.
51117
+ const retained = getHostLifecycleRecoveryUserAction(
51118
+ hostPreferencesState.update
51119
+ );
51120
+ if (
51121
+ retained &&
51122
+ ["complete_update_and_restart", "repair_and_restart"].includes(
51123
+ retained.kind
51124
+ )
51152
51125
  ) {
51126
+ openHostLifecycleRecoveryConfirmation(retained);
51127
+ return;
51128
+ }
51129
+ if (isOysterunUpdateOperationBlocking(hostPreferencesState.update)) {
51153
51130
  return;
51154
51131
  }
51155
51132
  const update = hostPreferencesState.update || {};
@@ -51233,10 +51210,11 @@ Waiting for Claude login output…</pre
51233
51210
  };
51234
51211
  renderHostPreferences();
51235
51212
  window.setTimeout(() => {
51236
- const recoveryMode =
51237
- HOST_LIFECYCLE_RECOVERY_MODES.includes(confirmationMode);
51213
+ // Return focus to the entry that opened the confirmation.
51238
51214
  $(
51239
- recoveryMode
51215
+ confirmationMode === "restart_only"
51216
+ ? "host-restart-btn"
51217
+ : confirmationMode === "restore_and_restart"
51240
51218
  ? "host-update-recovery-btn"
51241
51219
  : confirmationMode === "beta"
51242
51220
  ? "host-update-beta-run-btn"
@@ -51588,19 +51566,6 @@ Waiting for Claude login output…</pre
51588
51566
  renderHostPreferences();
51589
51567
  return;
51590
51568
  }
51591
- const confirmProviderPermissionMode =
51592
- requiresProviderPermissionPolicyConfirmation(
51593
- "claude",
51594
- currentDraft.claudePermissionMode
51595
- ) ||
51596
- requiresProviderPermissionPolicyConfirmation(
51597
- "codex",
51598
- currentDraft.codexApprovalPolicy
51599
- ) ||
51600
- requiresProviderPermissionPolicyConfirmation(
51601
- "xai",
51602
- currentDraft.xaiApprovalPolicy
51603
- );
51604
51569
  const preferencesPayload = serializeHostPreferencesDraft(currentDraft);
51605
51570
  hostPreferencesState = {
51606
51571
  ...hostPreferencesState,
@@ -51609,9 +51574,6 @@ Waiting for Claude login output…</pre
51609
51574
  };
51610
51575
  renderHostPreferences();
51611
51576
  try {
51612
- if (confirmProviderPermissionMode) {
51613
- preferencesPayload.confirm_beta_provider_permission_mode = true;
51614
- }
51615
51577
  const data = await apiWithAuthRetry(
51616
51578
  "PUT",
51617
51579
  "/dev/preferences",
@@ -63731,14 +63693,6 @@ Waiting for Claude login output…</pre
63731
63693
  if (isAllowedPathDebugUiVisible()) {
63732
63694
  payload.allowed_paths = allowedPaths;
63733
63695
  }
63734
- if (
63735
- requiresProviderPermissionPolicyConfirmation(
63736
- createRuntime,
63737
- permissionMode || codexApproval || xaiApproval
63738
- )
63739
- ) {
63740
- payload.confirm_beta_provider_permission_mode = true;
63741
- }
63742
63696
  validateTelegramEnabledPayloadOrThrow(
63743
63697
  payload,
63744
63698
  getCreateTelegramConfiguredState()
@@ -66143,8 +66097,6 @@ Waiting for Claude login output…</pre
66143
66097
  window.removeHostVoiceInputSavedToken = removeHostVoiceInputSavedToken;
66144
66098
  window.testHostVoiceInput = testHostVoiceInput;
66145
66099
  window.generateDirectHostLoginQr = generateDirectHostLoginQr;
66146
- window.copyHostPreferencesDirectHostUrl =
66147
- copyHostPreferencesDirectHostUrl;
66148
66100
  window.copyHostPreferencesLocalUrl = copyHostPreferencesLocalUrl;
66149
66101
  window.restartHostFromPreferences = restartHostFromPreferences;
66150
66102
  window.restorePreviousSessions = restorePreviousSessions;