@deepseek-insight/dsi 0.8.4 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.agents/skills/glass-dsi/SKILL.md +195 -0
  2. package/.agents/skills/glass-dsi/references/cdp-verification.md +132 -0
  3. package/.agents/skills/glass-dsi/scripts/_connect.mjs +81 -0
  4. package/.agents/skills/glass-dsi/scripts/glass-url.mjs +48 -0
  5. package/.agents/skills/glass-dsi/scripts/glass.mjs +84 -0
  6. package/.agents/skills/glass-url/SKILL.md +117 -0
  7. package/.agents/skills/glass-url/scripts/_args.mjs +49 -0
  8. package/.agents/skills/glass-url/scripts/_connect.mjs +99 -0
  9. package/.agents/skills/glass-url/scripts/glass-url.mjs +114 -0
  10. package/bin/dsi.mjs +23 -74
  11. package/bin/lib/zai-patch.mjs +115 -0
  12. package/build/client/_app/immutable/assets/0.Ch5VtsKp.css +2 -0
  13. package/build/client/_app/immutable/assets/2.D0QOh44D.css +1 -0
  14. package/build/client/_app/immutable/chunks/{DwcY-69G.js → 02Wfo2G3.js} +1 -1
  15. package/build/client/_app/immutable/chunks/0Tfhif1V.js +3 -0
  16. package/build/client/_app/immutable/chunks/{GKkjunMm.js → 0ruuBBbh.js} +2 -2
  17. package/build/client/_app/immutable/chunks/{DUCaFYlx.js → 4bHE1npv.js} +1 -1
  18. package/build/client/_app/immutable/chunks/{4cwyag_I.js → B4hDUAJl.js} +1 -1
  19. package/build/client/_app/immutable/chunks/{Cr2ck_ss.js → BIPXB-2I.js} +1 -1
  20. package/build/client/_app/immutable/chunks/{CeLBjOd9.js → BIgv-kQR.js} +1 -1
  21. package/build/client/_app/immutable/chunks/BK6k1nBs.js +1 -0
  22. package/build/client/_app/immutable/chunks/{SN3Efo-Q.js → BKwmKcDY.js} +1 -1
  23. package/build/client/_app/immutable/chunks/{CRFuOqJI.js → BORjuqef.js} +1 -1
  24. package/build/client/_app/immutable/chunks/{CH_bR_0j.js → BPmZB0-N.js} +1 -1
  25. package/build/client/_app/immutable/chunks/BROtno1Y.js +1 -0
  26. package/build/client/_app/immutable/chunks/{DEVxi5CK.js → BT5Iel00.js} +1 -1
  27. package/build/client/_app/immutable/chunks/{DdI16loP.js → BX3y_1Hh.js} +1 -1
  28. package/build/client/_app/immutable/chunks/{DuiFyTVb.js → Bea1gxLq.js} +1 -1
  29. package/build/client/_app/immutable/chunks/{D0uxHECr.js → BpkpgvVw.js} +1 -1
  30. package/build/client/_app/immutable/chunks/{CB55Rp7-.js → BreZS7EW.js} +1 -1
  31. package/build/client/_app/immutable/chunks/{BmDADyfH.js → BsS_byzk.js} +1 -1
  32. package/build/client/_app/immutable/chunks/{BWRXzjmQ.js → Bslg-dr5.js} +1 -1
  33. package/build/client/_app/immutable/chunks/{BkYT72a1.js → C-YczRN1.js} +1 -1
  34. package/build/client/_app/immutable/chunks/{DBKMIEwD.js → C2aW4lzo.js} +1 -1
  35. package/build/client/_app/immutable/chunks/{C4gs6zoI.js → C2qFXaz5.js} +1 -1
  36. package/build/client/_app/immutable/chunks/{BBd0nTHG.js → C5RyRpOh.js} +1 -1
  37. package/build/client/_app/immutable/chunks/{DJQr4efH.js → C5iHDVU_.js} +1 -1
  38. package/build/client/_app/immutable/chunks/{B4F5liKq.js → C7opVlLF.js} +1 -1
  39. package/build/client/_app/immutable/chunks/{DIxy2USu.js → CAuEkmZ4.js} +1 -1
  40. package/build/client/_app/immutable/chunks/{BO99LSSA.js → CIkiqgtA.js} +1 -1
  41. package/build/client/_app/immutable/chunks/{Bpd0I3U7.js → CJesWaZn.js} +1 -1
  42. package/build/client/_app/immutable/chunks/{at6PQqQ4.js → CRsoS5Y9.js} +1 -1
  43. package/build/client/_app/immutable/chunks/CTWtsoJD.js +6 -0
  44. package/build/client/_app/immutable/chunks/CWk5gmU2.js +1 -0
  45. package/build/client/_app/immutable/chunks/{lF8CNVOA.js → CXkrluSK.js} +1 -1
  46. package/build/client/_app/immutable/chunks/{DmRY6FWm.js → C_8kFZ_X.js} +4 -4
  47. package/build/client/_app/immutable/chunks/{D6KenyiF.js → Ch7hUoAH.js} +1 -1
  48. package/build/client/_app/immutable/chunks/{CnzmIbjr.js → ChmmHPdM.js} +1 -1
  49. package/build/client/_app/immutable/chunks/{DfUgh5E_.js → CjajmcJs.js} +1 -1
  50. package/build/client/_app/immutable/chunks/{q5PRaRZk.js → Ck6GS6Vb.js} +1 -1
  51. package/build/client/_app/immutable/chunks/{eFcFJ9Ld.js → ClGoDr_f.js} +1 -1
  52. package/build/client/_app/immutable/chunks/{BQC6O_RV.js → ClqGHlec.js} +1 -1
  53. package/build/client/_app/immutable/chunks/{zypfUZYM.js → CnW6hje6.js} +1 -1
  54. package/build/client/_app/immutable/chunks/{OW5Q7tGb.js → CpI6VYCH.js} +1 -1
  55. package/build/client/_app/immutable/chunks/{BXKc4uuX.js → CygwwaiE.js} +1 -1
  56. package/build/client/_app/immutable/chunks/{Bqwvv7Vf.js → CyhZtsZO.js} +35 -35
  57. package/build/client/_app/immutable/chunks/{BenkQ_4r.js → D-E2Noqq.js} +1 -1
  58. package/build/client/_app/immutable/chunks/{DuRZnRVw.js → D4Btzp_A.js} +1 -1
  59. package/build/client/_app/immutable/chunks/{CdaJvl_A.js → D4C3Jijv.js} +1 -1
  60. package/build/client/_app/immutable/chunks/D9jhmf3I.js +1 -0
  61. package/build/client/_app/immutable/chunks/{Cf0aMC4n.js → DCw0gAtv.js} +1 -1
  62. package/build/client/_app/immutable/chunks/{B6bTO8_7.js → DEW3KpiC.js} +1 -1
  63. package/build/client/_app/immutable/chunks/{O6xxeEm8.js → DYLFbKJe.js} +1 -1
  64. package/build/client/_app/immutable/chunks/{BsBBq-Qk.js → DYu7kgVa.js} +1 -1
  65. package/build/client/_app/immutable/chunks/Dfexc5qV.js +1 -0
  66. package/build/client/_app/immutable/chunks/{kzciT-3i.js → DhmUE8Ri.js} +1 -1
  67. package/build/client/_app/immutable/chunks/{DdJNsJBH.js → DjwX3rAC.js} +1 -1
  68. package/build/client/_app/immutable/chunks/{CajOZJDl.js → DlOfy8Sx.js} +1 -1
  69. package/build/client/_app/immutable/chunks/{CK5vhXvt.js → Do7Y1_MQ.js} +1 -1
  70. package/build/client/_app/immutable/chunks/{ap4GAVq-.js → Dq2XIb1m.js} +1 -1
  71. package/build/client/_app/immutable/chunks/{Czey8tDK.js → DuT_Qr5k.js} +1 -1
  72. package/build/client/_app/immutable/chunks/{4B6J_kM2.js → DyGggFea.js} +1 -1
  73. package/build/client/_app/immutable/chunks/{D0ChUnpi.js → Dym4i64U.js} +1 -1
  74. package/build/client/_app/immutable/chunks/FPsIAiN_.js +1 -0
  75. package/build/client/_app/immutable/chunks/{CevKuuic.js → GIczpP_D.js} +1 -1
  76. package/build/client/_app/immutable/chunks/{B2jrDnt8.js → LjOLSmU7.js} +1 -1
  77. package/build/client/_app/immutable/chunks/VtK73aN-.js +8 -0
  78. package/build/client/_app/immutable/chunks/{AcmOeh2U.js → ZqoQH_jm.js} +1 -1
  79. package/build/client/_app/immutable/chunks/{BlwWx85l.js → aXn6rE_7.js} +1 -1
  80. package/build/client/_app/immutable/chunks/{Czc4_W7v.js → dUOeoGwg.js} +1 -1
  81. package/build/client/_app/immutable/chunks/f50lp1rr.js +1 -0
  82. package/build/client/_app/immutable/chunks/{7FnrqGK9.js → lvLrv1cG.js} +1 -1
  83. package/build/client/_app/immutable/chunks/{CZoElI5B.js → pt3_jHwh.js} +1 -1
  84. package/build/client/_app/immutable/chunks/{Byu_teLA.js → q8irUX-p.js} +1 -1
  85. package/build/client/_app/immutable/chunks/{CnxoqrkQ.js → qmOgUo3c.js} +1 -1
  86. package/build/client/_app/immutable/chunks/vpNeypNM.js +1 -0
  87. package/build/client/_app/immutable/chunks/{DU8C5thd.js → xvTPLhcB.js} +1 -1
  88. package/build/client/_app/immutable/chunks/{DFBKiA2Z.js → zp7hrpsC.js} +1 -1
  89. package/build/client/_app/immutable/entry/app.D4oc_iez.js +2 -0
  90. package/build/client/_app/immutable/entry/start.Xkr2MCWg.js +1 -0
  91. package/build/client/_app/immutable/nodes/0.fsrqKU9i.js +1 -0
  92. package/build/client/_app/immutable/nodes/1.3F37GwcO.js +1 -0
  93. package/build/client/_app/immutable/nodes/2.DsWNNw0K.js +340 -0
  94. package/build/client/_app/version.json +1 -1
  95. package/build/handler.js +6 -6
  96. package/build/index.js +6 -6
  97. package/build/server/chunks/chunks/{app-config.svelte.js-BM4WbMFB.js → app-config.svelte.js-BXjvTmJu.js} +8 -3
  98. package/build/server/chunks/chunks/{config.js-vThvQeMn.js → config.js-r_jw_bQQ.js} +54 -2
  99. package/build/server/chunks/chunks/{db.js-DQm6CZ_r.js → db.js-f2PmBjyD.js} +2 -2
  100. package/build/server/chunks/chunks/{db2.js-DUhFxA3a.js → db2.js-DkwY64WO.js} +2 -2
  101. package/build/server/chunks/chunks/{dsh-connection.js-BgeraFA2.js → dsh-connection.js-BW07Aicl.js} +18 -10
  102. package/build/server/chunks/chunks/{dsh-events.js-BUkLR6ZV.js → dsh-events.js-BHf0lLZ5.js} +16 -3
  103. package/build/server/chunks/chunks/{git-probe.js-CLF37SRh.js → git-probe.js-CQqTV7XX.js} +3 -3
  104. package/build/server/chunks/chunks/{index-server.js-DmIEbDVz.js → index-server.js-CPkVoRxB.js} +2 -2
  105. package/build/server/chunks/chunks/{insight-config.js-Dp_2gB5J.js → insight-config.js-C5-Rhh5A.js} +19 -6
  106. package/build/server/chunks/chunks/{internal2.js-J3h-US64.js → internal2.js-DbC8VGqD.js} +4 -4
  107. package/build/server/chunks/chunks/ledger-stats.js-BWACyDPg.js +72 -0
  108. package/build/server/chunks/chunks/{registry.js-CZNJZ0C-.js → registry.js-CWnRQ4vx.js} +751 -53
  109. package/build/server/chunks/chunks/{registry2.js-D3Pr1Rv9.js → registry2.js-CFzpvj9c.js} +17 -1
  110. package/build/server/chunks/chunks/{server.js-B1bDXhHu.js → server.js-Dijo7DtV.js} +2 -2
  111. package/build/server/chunks/chunks/{settings-document.js-Gw1puaik.js → settings-document.js-DxKURfc_.js} +2 -2
  112. package/build/server/chunks/chunks/{state.js-8RBD56dL.js → state.js-DR4xiIVw.js} +5 -5
  113. package/build/server/chunks/chunks/{watcher.js-BVKnoQUw.js → watcher.js-DvlNn8X0.js} +5 -5
  114. package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-BjzdVwdY.js → _server.ts.js-DItDXV23.js} +5 -5
  115. package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-3kQCQuHT.js → _server.ts.js-Hor8QZb1.js} +8 -8
  116. package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-BUBTCLFn.js → _server.ts.js-CEvvsa8B.js} +8 -8
  117. package/build/server/chunks/entries/endpoints/api/browser/_id_/activate/{_server.ts.js-BdMJzENm.js → _server.ts.js-sXqf-okw.js} +5 -5
  118. package/build/server/chunks/entries/endpoints/api/browser/_id_/close/{_server.ts.js-B5z9-N8Y.js → _server.ts.js-Byj9ApJh.js} +5 -5
  119. package/build/server/chunks/entries/endpoints/api/browser/_id_/closetab/{_server.ts.js-CnZHun5r.js → _server.ts.js-DOIsrzvg.js} +16 -9
  120. package/build/server/chunks/entries/endpoints/api/browser/_id_/element/{_server.ts.js-C1oM2Ihf.js → _server.ts.js-Cq3gmRfE.js} +5 -5
  121. package/build/server/chunks/entries/endpoints/api/browser/_id_/history/_server.ts.js-BGSLIV0t.js +61 -0
  122. package/build/server/chunks/entries/endpoints/api/browser/_id_/input/{_server.ts.js-DmbqGRMg.js → _server.ts.js-8slca75-.js} +5 -5
  123. package/build/server/chunks/entries/endpoints/api/browser/_id_/mcp/{_server.ts.js-Cj80T1yH.js → _server.ts.js-CpbdTYds.js} +5 -5
  124. package/build/server/chunks/entries/endpoints/api/browser/_id_/navigate/{_server.ts.js-6gi1UYTJ.js → _server.ts.js-Bls9LtLY.js} +5 -5
  125. package/build/server/chunks/entries/endpoints/api/browser/_id_/opentab/{_server.ts.js-Bhx_J7ot.js → _server.ts.js-DqNi_iW_.js} +5 -5
  126. package/build/server/chunks/entries/endpoints/api/browser/_id_/page/{_server.ts.js-DvQXNnX9.js → _server.ts.js-DuXWwo7i.js} +5 -5
  127. package/build/server/chunks/entries/endpoints/api/browser/_id_/stream/{_server.ts.js-MflkVWUn.js → _server.ts.js-DXfqBDEw.js} +38 -9
  128. package/build/server/chunks/entries/endpoints/api/browser/_id_/tabs/{_server.ts.js-CoUSlLcP.js → _server.ts.js-Bgnl8lYs.js} +5 -5
  129. package/build/server/chunks/entries/endpoints/api/browser/_id_/viewport/{_server.ts.js-DNk4EgjJ.js → _server.ts.js-FHmQVbrv.js} +5 -5
  130. package/build/server/chunks/entries/endpoints/api/browser/_id_/zoom/_server.ts.js-BV4cBtSA.js +62 -0
  131. package/build/server/chunks/entries/endpoints/api/browser/{_server.ts.js-CECKap7O.js → _server.ts.js-4vgkwh7v.js} +9 -6
  132. package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-C8XDnDwe.js → _server.ts.js-C_9kwANR.js} +16 -6
  133. package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-BFmWshpv.js → _server.ts.js-h1OTHcL4.js} +5 -5
  134. package/build/server/chunks/entries/endpoints/api/dsh/jobs/{_server.ts.js-DATWy5mQ.js → _server.ts.js-B6nih9Sd.js} +5 -5
  135. package/build/server/chunks/entries/endpoints/api/dsh/jobs/follow/{_server.ts.js-CugOD1Zd.js → _server.ts.js-Cf17SVmD.js} +5 -5
  136. package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-DCupM4da.js → _server.ts.js-DjNwMbdS.js} +5 -5
  137. package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-Bh1btvr9.js → _server.ts.js-Cz5aaEhC.js} +5 -5
  138. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-BFBy5O9Y.js +78 -0
  139. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-WI9yH2iG.js → _server.ts.js-CgEdCo3T.js} +5 -5
  140. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-bN9adUqz.js → _server.ts.js-Bo_aEnHh.js} +5 -5
  141. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-Bxlm966G.js → _server.ts.js-Bd2G_GaH.js} +5 -5
  142. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-CM-raN_Q.js → _server.ts.js-DL64FyRw.js} +5 -5
  143. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-BzP30TXW.js → _server.ts.js-MhOGB66D.js} +7 -70
  144. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-DvNkrTaY.js → _server.ts.js-MjPgXgGO.js} +5 -5
  145. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-DXbCrefw.js → _server.ts.js-BUjDae5H.js} +5 -5
  146. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-B7YRQhuJ.js → _server.ts.js-dTKd7Hsv.js} +6 -6
  147. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-C_AEaLLJ.js → _server.ts.js-B82rvKoI.js} +5 -5
  148. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-Dj-pF5ik.js → _server.ts.js-Cqijsi-c.js} +4 -4
  149. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-BX9FWJ6g.js → _server.ts.js-CL_vVv2p.js} +29 -6
  150. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-CQ2Bmtu4.js → _server.ts.js-WS4W9Ly4.js} +5 -5
  151. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-Ha7xjnKW.js → _server.ts.js-B72KGRGh.js} +5 -5
  152. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-BOg6llHk.js → _server.ts.js-DK5xb2b5.js} +5 -5
  153. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-DBqGALzZ.js → _server.ts.js-lhT87EQu.js} +5 -5
  154. package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-CvA8WDDb.js → _server.ts.js-CMhfhbkN.js} +5 -5
  155. package/build/server/chunks/entries/endpoints/api/dsh/subagents/_server.ts.js-BLvcFsg6.js +72 -0
  156. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-BfjNH40j.js → _server.ts.js-D_rw2Uq6.js} +5 -5
  157. package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-De_qumyr.js → _server.ts.js-DfXpMMpw.js} +5 -5
  158. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-tFpJJHCb.js → _server.ts.js-2mJCJVxk.js} +5 -5
  159. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-DVx5GGA_.js → _server.ts.js-BCx0Lzjf.js} +5 -5
  160. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-DuKT-aZp.js → _server.ts.js-DECaSjwp.js} +5 -5
  161. package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-DsWFidFY.js → _server.ts.js-i_ciJzlb.js} +5 -5
  162. package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-C6j0adQs.js → _server.ts.js-BO1pScn7.js} +5 -5
  163. package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-BChAg4hm.js → _server.ts.js-Buhpf7WE.js} +5 -5
  164. package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-BPSHqbZN.js → _server.ts.js-DngUQzcT.js} +4 -4
  165. package/build/server/chunks/entries/endpoints/api/terminal/_id_/output/{_server.ts.js-BNBxebgG.js → _server.ts.js-DLETP4yQ.js} +2 -2
  166. package/build/server/chunks/entries/endpoints/api/terminal/_id_/reattach/{_server.ts.js-DDRJBGIr.js → _server.ts.js-uWl6oxPS.js} +2 -2
  167. package/build/server/chunks/entries/endpoints/api/terminal/_id_/send/{_server.ts.js-CA0vc_Cm.js → _server.ts.js-DYNmXaAd.js} +9 -2
  168. package/build/server/chunks/entries/endpoints/api/terminal/_id_/stream/{_server.ts.js-D6L8Gd_f.js → _server.ts.js-C5m3Ktho.js} +2 -2
  169. package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-D5CbUe5k.js → _server.ts.js-DoTNvcOa.js} +5 -5
  170. package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-B-5YihUA.js → _server.ts.js-BzYOJSkI.js} +7 -7
  171. package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-DT5jRZFE.js → _server.ts.js-xcNesei9.js} +7 -7
  172. package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-DtW-utrd.js → _server.ts.js-DsJWYdxt.js} +7 -7
  173. package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-8AIAo9jX.js → _server.ts.js-CNCku9BR.js} +7 -7
  174. package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-CMw96vjY.js → _server.ts.js-nLsNjOxj.js} +7 -7
  175. package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-DKufa14_.js → _server.ts.js-urQk2-ds.js} +7 -7
  176. package/build/server/chunks/entries/{hooks.server.js-B-e4_ekh.js → hooks.server.js-CU2RE5vs.js} +3 -3
  177. package/build/server/chunks/entries/pages/{_error.svelte.js-DRPduJdP.js → _error.svelte.js-e018KpwE.js} +6 -6
  178. package/build/server/chunks/entries/pages/{_page.server.ts.js-7x6ZfGoB.js → _page.server.ts.js-BK3Y6eff.js} +3 -3
  179. package/build/server/chunks/entries/pages/{_page.svelte.js-Dek8Q1_v.js → _page.svelte.js-DQWBiRk1.js} +4185 -1734
  180. package/build/server/chunks/{handler-DUyvzXsb.js → handler-DsD7I1_u.js} +3 -3
  181. package/build/server/chunks/{index.js-BrP1SRzN.js → index.js-C_WAkwJs.js} +4 -4
  182. package/build/server/chunks/{manifest.js-C4z6aoYx.js → manifest.js-DUy52VaP.js} +85 -64
  183. package/build/server/chunks/nodes/{0.js-BuB3t8Br.js → 0.js-CuV3dXVD.js} +3 -3
  184. package/build/server/chunks/nodes/1.js-Dc5QC-NJ.js +9 -0
  185. package/build/server/chunks/nodes/{2.js-C-o9KF10.js → 2.js-PwcF4_kT.js} +10 -10
  186. package/package.json +2 -2
  187. package/skills/{dsi-kb-scenario → dsi-fc-scenario}/SKILL.md +11 -6
  188. package/template/.dsi/prompts.sqlite +0 -0
  189. package/template/.dsi/prompts.sqlite-shm +0 -0
  190. package/template/.dsi/prompts.sqlite-wal +0 -0
  191. package/template/.dsi/settings.yaml +7 -2
  192. package/build/client/_app/immutable/assets/0.cswkTiBk.css +0 -2
  193. package/build/client/_app/immutable/assets/2.BPprd30B.css +0 -1
  194. package/build/client/_app/immutable/chunks/BHDqH9nE.js +0 -1
  195. package/build/client/_app/immutable/chunks/C7Sg_49n.js +0 -1
  196. package/build/client/_app/immutable/chunks/CAneZX4t.js +0 -8
  197. package/build/client/_app/immutable/chunks/D-Rolhzs.js +0 -1
  198. package/build/client/_app/immutable/chunks/DQXo7qdJ.js +0 -1
  199. package/build/client/_app/immutable/chunks/DY0MQ1mO.js +0 -1
  200. package/build/client/_app/immutable/chunks/K3xyLTzZ.js +0 -1
  201. package/build/client/_app/immutable/chunks/NcOjFWr3.js +0 -6
  202. package/build/client/_app/immutable/chunks/cHj8EuyS.js +0 -3
  203. package/build/client/_app/immutable/chunks/o7DVZODA.js +0 -1
  204. package/build/client/_app/immutable/chunks/pdYhlwhf.js +0 -1
  205. package/build/client/_app/immutable/entry/app.DYNedUxy.js +0 -2
  206. package/build/client/_app/immutable/entry/start.BzyrBssE.js +0 -1
  207. package/build/client/_app/immutable/nodes/0.DQYP6BCS.js +0 -1
  208. package/build/client/_app/immutable/nodes/1.CHB5qqQ9.js +0 -1
  209. package/build/client/_app/immutable/nodes/2.C6No1310.js +0 -340
  210. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-Cb8SBWVw.js +0 -46
  211. package/build/server/chunks/nodes/1.js-CW4V8ni8.js +0 -9
@@ -1,4 +1,4 @@
1
- import { d as readBrowserConfig } from './insight-config.js-Dp_2gB5J.js';
1
+ import { d as readBrowserConfig } from './insight-config.js-C5-Rhh5A.js';
2
2
  import { mkdirSync, rmSync, readdirSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { spawn, spawnSync } from 'node:child_process';
@@ -157,6 +157,29 @@ var BrowserRelay = class {
157
157
  }
158
158
  };
159
159
  Number(process.env.DSI_MCP_PORT ?? 9515);
160
+ /** Layout-scale zoom (ADR-0041 amendment, 2026-10-31): the zoom knob is
161
+ * LAYOUT SCALE — the emulated viewport is the panel box divided by the
162
+ * zoom, and the mirror's contain-fit does the visual shrinking. The page
163
+ * keeps ONE coordinate space (no CSS zoom), so every consumer's own
164
+ * geometry math — YouTube's seek preview included — stays truthful.
165
+ * Rejected: documentElement CSS zoom (the 2026-10-28..31 implementation) —
166
+ * Chromium hit-testing stayed correct but the PAGE's own width perception
167
+ * split in two, and YouTube's player computed seek previews at exactly
168
+ * the zoom factor off (54:58 vs 1:08:44 at 0.8). */
169
+ function layoutViewport(width, height, zoom) {
170
+ const z = zoom > 0 ? zoom : 1;
171
+ return {
172
+ width: Math.max(200, Math.min(8192, Math.round(width / z))),
173
+ height: Math.max(120, Math.min(8192, Math.round(height / z)))
174
+ };
175
+ }
176
+ /** Live zoom slider (2026-10-28): the slider's wire bounds — null when the
177
+ * value is not a finite number in 0.5..1.25 (the route answers 400). */
178
+ function clampZoom(z) {
179
+ if (typeof z !== "number" || !Number.isFinite(z)) return null;
180
+ if (z < .5 || z > 1.25) return null;
181
+ return z;
182
+ }
160
183
  /** The reaper's ONLY test — pure: a session is reaper-eligible when nobody
161
184
  * watches its stream AND its last MCP tool traffic is older than idleMs.
162
185
  * An active agent (traffic younger than idleMs) is NEVER eligible (D2). */
@@ -164,6 +187,100 @@ function reaperEligible(e, idleMs, now) {
164
187
  if (e.streamSubscribers > 0) return false;
165
188
  return now - e.lastMcpActivity >= idleMs;
166
189
  }
190
+ /** Mint the next ordinal for targetId (idempotent: an already-minted
191
+ * targetId returns its existing ordinal — lazy attach and heal re-enter). */
192
+ function mintTabId(entry, targetId) {
193
+ const tabIds = entry.tabIds ??= /* @__PURE__ */ new Map();
194
+ for (const [ord, tid] of tabIds) if (tid === targetId) return ord;
195
+ entry.nextTabId ??= 1;
196
+ const ord = entry.nextTabId;
197
+ entry.nextTabId = ord + 1;
198
+ entry.tabIds.set(ord, targetId);
199
+ return ord;
200
+ }
201
+ /** ADR-0040 Amendment 4 (D-A5): the OWNER hand claims a newborn. Called
202
+ * from the entry's own context 'page' listener — a claimed targetId is
203
+ * owner-born no matter what the browser-level watcher saw first. Pure. */
204
+ function claimOwnerBirth(entry, targetId) {
205
+ (entry.ownBirths ??= /* @__PURE__ */ new Set()).add(targetId);
206
+ }
207
+ /** ADR-0040 Amendment 4 (D-A6): a GUEST birth is a citizen without the
208
+ * throne — mint an ordinal, append tabOrder (lands LAST), and NEVER move
209
+ * the active pointer (appear without stealing focus). Guards: closed
210
+ * sessions refuse; a target the entry already knows (or the owner hand
211
+ * claimed) is a no-op. Pure. Returns true when the guest was admitted. */
212
+ function admitGuestBirth(e, targetId) {
213
+ if (e.closed) return false;
214
+ if (e.ownBirths?.has(targetId)) return false;
215
+ if (e.tabOrder.includes(targetId) || e.tabs.has(targetId)) return false;
216
+ e.tabOrder.push(targetId);
217
+ mintTabId(e, targetId);
218
+ return true;
219
+ }
220
+ /** Retire a closed tab: drop both map sides. The ordinal itself is gone —
221
+ * mintTabId never hands it out again. */
222
+ function retireTabId(entry, targetId) {
223
+ if (!entry.tabIds) return;
224
+ for (const [ord, tid] of entry.tabIds) if (tid === targetId) {
225
+ entry.tabIds.delete(ord);
226
+ return;
227
+ }
228
+ }
229
+ /** HEAL PRESERVES ORDINALS: healHandles swaps a dead CDP handle for a
230
+ * fresh session — the tab's ordinal must survive with its value
231
+ * rewritten to the new targetId. An unknown page (first sight during
232
+ * heal) falls back to a fresh mint. */
233
+ function healPreserveOrdinal(entry, oldTargetId, newTargetId) {
234
+ const tabIds = entry.tabIds ??= /* @__PURE__ */ new Map();
235
+ for (const [ord, tid] of tabIds) if (tid === oldTargetId) {
236
+ entry.tabIds.set(ord, newTargetId);
237
+ return ord;
238
+ }
239
+ return mintTabId(entry, newTargetId);
240
+ }
241
+ /** Resolve a tab reference — ordinal (number / numeric string), the
242
+ * 'active' pointer, or a raw targetId — to { targetId, tabId }. A
243
+ * retired or unknown ordinal is an EXPLICIT error: no silent
244
+ * nearest-match (glass-tab-ids global constraint). */
245
+ function resolveTab(entry, ref) {
246
+ if (ref === "active") {
247
+ if (!entry.activeTargetId) return {
248
+ ok: false,
249
+ error: "no active tab"
250
+ };
251
+ for (const [ord, tid] of entry.tabIds ?? []) if (tid === entry.activeTargetId) return {
252
+ ok: true,
253
+ targetId: tid,
254
+ tabId: ord
255
+ };
256
+ return {
257
+ ok: false,
258
+ error: "active tab has no ordinal: " + entry.activeTargetId
259
+ };
260
+ }
261
+ if (typeof ref === "number" || /^\d+$/.test(ref)) {
262
+ const ord = Number(ref);
263
+ const tid = entry.tabIds?.get(ord);
264
+ if (tid === void 0) return {
265
+ ok: false,
266
+ error: "unknown or retired tab ordinal: " + ord
267
+ };
268
+ return {
269
+ ok: true,
270
+ targetId: tid,
271
+ tabId: ord
272
+ };
273
+ }
274
+ for (const [ord, tid] of entry.tabIds ?? []) if (tid === ref) return {
275
+ ok: true,
276
+ targetId: tid,
277
+ tabId: ord
278
+ };
279
+ return {
280
+ ok: false,
281
+ error: "unknown tab: " + ref
282
+ };
283
+ }
167
284
  var BrowserRegistryRefusal = class extends Error {
168
285
  code;
169
286
  constructor(code, message) {
@@ -190,10 +307,52 @@ function state() {
190
307
  function sweepOrphanProfiles(keep) {
191
308
  try {
192
309
  const root = readBrowserConfig().profileRoot;
193
- for (const name of readdirSync(root)) if (!keep.has(name)) rmSync(join(root, name), {
194
- recursive: true,
195
- force: true
196
- });
310
+ for (const name of readdirSync(root)) {
311
+ if (keep.has(name)) continue;
312
+ if (!process.env.VITEST) killOrphanChrome(name, {
313
+ listPids: () => spawnSync("ps", ["axo", "pid=,command="], {
314
+ encoding: "utf8",
315
+ timeout: 5e3
316
+ }).stdout ?? "",
317
+ killPid: (pid, sig) => process.kill(pid, sig)
318
+ });
319
+ rmSync(join(root, name), {
320
+ recursive: true,
321
+ force: true
322
+ });
323
+ }
324
+ } catch {}
325
+ }
326
+ /** Ghost Glass Fix 3 (RCA 2026-10-31 Bug 3): PURE matcher — which process
327
+ * rows hold a Chrome --user-data-dir that ENDS at the swept profile name?
328
+ * The name must terminate the dir (path separator before it, end/space
329
+ * after it), so a PREFIX-sharing sibling profile never matches. */
330
+ function orphanChromePids(psOutput, profileName) {
331
+ const needle = "--user-data-dir=";
332
+ const pids = [];
333
+ for (const line of psOutput.split("\n")) {
334
+ const at = line.indexOf(needle);
335
+ if (at === -1) continue;
336
+ const rest = line.slice(at + 16);
337
+ const end = rest.search(/[\s"']/);
338
+ if (!(end === -1 ? rest : rest.slice(0, end)).replace(/\/+$/, "").endsWith("/" + profileName)) continue;
339
+ const pid = Number.parseInt(line.trimStart(), 10);
340
+ if (Number.isFinite(pid)) pids.push(pid);
341
+ }
342
+ return pids;
343
+ }
344
+ /** Ghost Glass Fix 3: the sweep kill arm with the OS injected — SIGKILL
345
+ * every holder of the profile dir. Best-effort both ways: a failed ps
346
+ * listing or an already-dead pid (ESRCH) never blocks the dir sweep. */
347
+ function killOrphanChrome(profileName, deps) {
348
+ let out;
349
+ try {
350
+ out = deps.listPids();
351
+ } catch {
352
+ return;
353
+ }
354
+ for (const pid of orphanChromePids(out, profileName)) try {
355
+ deps.killPid(pid, "SIGKILL");
197
356
  } catch {}
198
357
  }
199
358
  /** Brain re-homing (BUGFIX 2026-10-25): release the stable brain port
@@ -313,6 +472,7 @@ var BrowserRegistry = class {
313
472
  pid: e.pid,
314
473
  profileDir: e.profileDir,
315
474
  createdAt: e.createdAt,
475
+ ownerSessionId: e.ownerSessionId ?? null,
316
476
  mcp: e.mcpChild ? "child" : e.mcpInProcess ? "in-process" : "none",
317
477
  mcpHealth: e.mcpHealth,
318
478
  mcpError: e.mcpError
@@ -366,7 +526,7 @@ var BrowserRegistry = class {
366
526
  const now = Date.now();
367
527
  for (const e of state().entries.values()) {
368
528
  if (!reaperEligible(e, idleMs, now)) continue;
369
- this.close(e.id).catch(() => void 0);
529
+ this.close(e.id, "reaped").catch(() => void 0);
370
530
  }
371
531
  }, Math.min(idleMs / 2, 3e4));
372
532
  s.reaperTimer.unref?.();
@@ -378,6 +538,31 @@ var BrowserRegistry = class {
378
538
  if (!h) throw new BrowserRegistryRefusal("CLOSED", "no active tab handle");
379
539
  return h.relay;
380
540
  }
541
+ /** D5 amendment 2026-10-27: watch the active pointer. The stream route
542
+ * re-binds its relay on every fire so a brain-driven activate (creation
543
+ * auto-focus) repoints the MIRROR, not just the strip highlight. */
544
+ /** Ghost Glass Fix 2 (RCA 2026-10-31): the death handshake's registry
545
+ * side. Subscribers hear ONE reason ('closed' human, 'reaped' reaper);
546
+ * subscribing to an already-dead id hears the truth immediately instead
547
+ * of polling a ghost. Same shape as onActiveChange. */
548
+ onDeath(id, cb) {
549
+ const e = state().entries.get(id);
550
+ if (!e || e.closed) {
551
+ cb("closed");
552
+ return () => void 0;
553
+ }
554
+ (e.deathListeners ??= /* @__PURE__ */ new Set()).add(cb);
555
+ return () => {
556
+ e.deathListeners?.delete(cb);
557
+ };
558
+ }
559
+ onActiveChange(id, cb) {
560
+ const e = this.peek(id);
561
+ (e.activeListeners ??= /* @__PURE__ */ new Set()).add(cb);
562
+ return () => {
563
+ e.activeListeners?.delete(cb);
564
+ };
565
+ }
381
566
  /** Resolve a verb's target handle: explicit targetId, else the active tab
382
567
  * (ADR-0040 D3 — one verb seam, optional pointer). An ENUMERATED but
383
568
  * not-yet-attached target attaches lazily here — attach is not stream
@@ -413,7 +598,12 @@ var BrowserRegistry = class {
413
598
  relay: handle.relay,
414
599
  page
415
600
  }).catch(() => void 0);
416
- e.tabs.set(tid, handle);
601
+ const ord = mintTabId(e, tid);
602
+ e.tabs.set(tid, {
603
+ ...handle,
604
+ tabId: ord
605
+ });
606
+ (e.tabOrder ??= []).push(tid);
417
607
  if (tid === key) break;
418
608
  }
419
609
  }
@@ -421,19 +611,137 @@ var BrowserRegistry = class {
421
611
  if (!h) throw new BrowserRegistryRefusal("NO_TARGET", "no such tab: " + key);
422
612
  return h;
423
613
  }
614
+ /** BUG 2026-10-27 (brain churn leaves stale handles): the registry's
615
+ * cached CDP sessions can DIE while the pages live on (brain re-home,
616
+ * MCP client reconnect) — listTabs then answers empty, every verb
617
+ * NO_TARGETs, and the mirror starves on one frozen frame. Heal: re-probe
618
+ * every live page with a FRESH CDP session, replace dead handles, and
619
+ * remap the pointer when its target is gone. Called from listTabs when
620
+ * enumeration comes back empty — the panel's 2s poll then heals the
621
+ * whole session without operator action. */
622
+ async healHandles(e) {
623
+ for (const page of e.context.pages()) {
624
+ let scdp = null;
625
+ let tid = null;
626
+ try {
627
+ scdp = await e.context.newCDPSession(page);
628
+ tid = (await scdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
629
+ } catch {
630
+ continue;
631
+ }
632
+ if (!tid || !scdp) {
633
+ await scdp?.detach().catch(() => void 0);
634
+ continue;
635
+ }
636
+ const old = e.tabs.get(tid);
637
+ if (old) {
638
+ if (!await old.cdp.send("Target.getTargetInfo").then(() => false).catch(() => true)) {
639
+ await scdp.detach().catch(() => void 0);
640
+ continue;
641
+ }
642
+ old.relay.stop();
643
+ await old.cdp.detach().catch(() => void 0);
644
+ const cfg = readBrowserConfig();
645
+ const ord = healPreserveOrdinal(e, tid, tid);
646
+ e.tabs.set(tid, {
647
+ targetId: tid,
648
+ tabId: ord,
649
+ cdp: scdp,
650
+ relay: new BrowserRelay(scdp, cfg.screencastQuality, cfg.screencastMaxFps),
651
+ page
652
+ });
653
+ if (!e.tabOrder.includes(tid)) e.tabOrder.push(tid);
654
+ } else await scdp.detach().catch(() => void 0);
655
+ }
656
+ if (e.activeTargetId && !e.tabs.has(e.activeTargetId)) e.activeTargetId = [...e.tabs.keys()][0] ?? null;
657
+ }
424
658
  /** Enumerate the context's page targets (ADR-0040 D5 feed) — type:page
425
659
  * only; iframes/omnibox/service workers are chrome noise (wire capture
426
- * 2026-10-24). Read-only: never attaches, never streams (D1). */
660
+ * 2026-10-24). Mirror-lifecycle (ADR "The Mirror Must Face a Living
661
+ * Tab", D1/D2): enumeration ADOPTS unknown live pages (mint + lazy
662
+ * seam — survivors' ordinals never move) and RE-POINTS the active
663
+ * pointer when its target is gone, re-seeding the mirror through
664
+ * activate's idiom — the panel never faces a corpse (the 2026-10-28
665
+ * frozen-frame bug). */
427
666
  async listTabs(id) {
428
667
  const e = this.peek(id);
429
- const src = (e.activeTargetId ? e.tabs.get(e.activeTargetId) : null)?.cdp ?? [...e.tabs.values()][0]?.cdp;
430
- if (!src) return [];
431
- return ((await src.send("Target.getTargets").catch(() => null))?.targetInfos ?? []).filter((t) => t.type === "page").map((t) => ({
668
+ const pickSrc = () => (e.activeTargetId ? e.tabs.get(e.activeTargetId) : null)?.cdp ?? [...e.tabs.values()][0]?.cdp ?? null;
669
+ const readTargets = async (h) => {
670
+ return (await h.send("Target.getTargets").catch(() => null))?.targetInfos ?? null;
671
+ };
672
+ let src = pickSrc();
673
+ let infos = (src ? await readTargets(src) : null) ?? [];
674
+ if (infos === null || infos.length === 0 && e.context.pages().length > 0) {
675
+ await this.healHandles(e);
676
+ src = pickSrc();
677
+ infos = (src ? await readTargets(src) : null) ?? [];
678
+ }
679
+ let pages = infos.filter((t) => t.type === "page");
680
+ if (pages.length === 0 && e.context.pages().length > 0) {
681
+ const probed = [];
682
+ for (const page of e.context.pages()) try {
683
+ const scdp = await e.context.newCDPSession(page);
684
+ const info = await scdp.send("Target.getTargetInfo");
685
+ await scdp.detach().catch(() => void 0);
686
+ if (info.targetInfo?.targetId) probed.push({
687
+ targetId: info.targetInfo.targetId,
688
+ type: "page",
689
+ url: info.targetInfo.url ?? page.url(),
690
+ title: info.targetInfo.title ?? ""
691
+ });
692
+ } catch {
693
+ continue;
694
+ }
695
+ pages = probed;
696
+ }
697
+ const known = new Set(e.tabs.keys());
698
+ const unknown = pages.filter((t) => !known.has(t.targetId));
699
+ if (unknown.length > 0) {
700
+ const cfg = readBrowserConfig();
701
+ for (const page of e.context.pages()) {
702
+ let tid = null;
703
+ let scdp = null;
704
+ try {
705
+ scdp = await e.context.newCDPSession(page);
706
+ tid = (await scdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
707
+ } catch {
708
+ continue;
709
+ }
710
+ if (!tid || !unknown.some((u) => u.targetId === tid)) {
711
+ await scdp.detach().catch(() => void 0);
712
+ continue;
713
+ }
714
+ const ord = mintTabId(e, tid);
715
+ if (!e.tabOrder.includes(tid)) e.tabOrder.push(tid);
716
+ e.tabs.set(tid, {
717
+ targetId: tid,
718
+ tabId: ord,
719
+ cdp: scdp,
720
+ relay: new BrowserRelay(scdp, cfg.screencastQuality, cfg.screencastMaxFps),
721
+ page
722
+ });
723
+ }
724
+ }
725
+ const out = pages.map((t) => ({
726
+ tabId: mintTabId(e, t.targetId),
432
727
  targetId: t.targetId,
433
728
  url: t.url,
434
729
  title: t.title,
435
730
  active: t.targetId === e.activeTargetId
436
- }));
731
+ })).sort((a, b) => {
732
+ const order = e.tabOrder ?? [];
733
+ const ia = order.indexOf(a.targetId);
734
+ const ib = order.indexOf(b.targetId);
735
+ return (ia === -1 ? Infinity : ia) - (ib === -1 ? Infinity : ib);
736
+ });
737
+ if (e.activeTargetId && !out.some((r) => r.targetId === e.activeTargetId)) {
738
+ const survivor = out[0]?.targetId ?? null;
739
+ if (survivor) {
740
+ await this.activate(id, survivor).catch(() => void 0);
741
+ for (const r of out) r.active = r.targetId === e.activeTargetId;
742
+ } else e.activeTargetId = null;
743
+ }
744
+ return out;
437
745
  }
438
746
  /** Attach (lazily), seed the first frame, re-apply the viewport, and move
439
747
  * the active pointer (ADR-0040 D1/D2/D4). The OLD active relay pauses —
@@ -462,6 +770,9 @@ var BrowserRegistry = class {
462
770
  });
463
771
  }
464
772
  e.activeTargetId = targetId;
773
+ for (const l of e.activeListeners ?? []) try {
774
+ l(targetId);
775
+ } catch {}
465
776
  let url = "";
466
777
  let title = "";
467
778
  try {
@@ -483,22 +794,25 @@ var BrowserRegistry = class {
483
794
  const port = freePort(options.port);
484
795
  const profileDir = join(cfg.profileRoot, id.slice(0, 8));
485
796
  mkdirSync(profileDir, { recursive: true });
486
- const ignoreDefaultArgs = ["--enable-automation", "--mute-audio"];
797
+ const ignoreDefaultArgs = [
798
+ "--enable-automation",
799
+ "--mute-audio",
800
+ "--hide-scrollbars"
801
+ ];
487
802
  const launch = (useChrome) => chromium.launchPersistentContext(profileDir, {
488
803
  ...useChrome ? { channel: "chrome" } : {},
489
804
  headless: options.headless ?? cfg.headless,
490
805
  ignoreDefaultArgs,
491
806
  args: [
492
- "--remote-debugging-port=" + port,
493
807
  "--no-first-run",
494
- "--disable-blink-features=AutomationControlled",
495
- "--disable-features=IsolateOrigins,site-per-process",
808
+ "--remote-debugging-port=" + port,
496
809
  "--disable-dev-shm-usage",
497
- "--disable-backgrounding-occluded-windows",
498
810
  "--disable-renderer-backgrounding",
499
811
  "--disable-background-timer-throttling",
812
+ "--disable-backgrounding-occluded-windows",
813
+ "--disable-blink-features=AutomationControlled",
814
+ "--disable-features=IsolateOrigins,site-per-process",
500
815
  "--autoplay-policy=no-user-gesture-required",
501
- "--window-position=-32000,-32000",
502
816
  "--window-size=" + (Math.max(200, Math.min(1920, options.width ?? 730)) + 6) + "," + (Math.max(120, Math.min(1080, options.height ?? 830)) + 70)
503
817
  ],
504
818
  viewport: options.headless ?? cfg.headless ? {
@@ -524,12 +838,19 @@ var BrowserRegistry = class {
524
838
  try {
525
839
  activeTargetId = (await cdp.send("Target.getTargetInfo")).targetInfo?.targetId ?? null;
526
840
  } catch {}
527
- if (activeTargetId) tabs.set(activeTargetId, {
528
- targetId: activeTargetId,
529
- cdp,
530
- relay,
531
- page: context.pages()[0]
532
- });
841
+ const tabIds = /* @__PURE__ */ new Map();
842
+ let nextTabId = 1;
843
+ if (activeTargetId) {
844
+ tabIds.set(1, activeTargetId);
845
+ nextTabId = 2;
846
+ tabs.set(activeTargetId, {
847
+ targetId: activeTargetId,
848
+ tabId: 1,
849
+ cdp,
850
+ relay,
851
+ page: context.pages()[0]
852
+ });
853
+ }
533
854
  await relay.start();
534
855
  const start = options.url ?? "data:text/html,<title>Glass Browser</title><body style=\"font-family:system-ui;color:gray;display:grid;place-items:center;height:100vh;margin:0\">Glass Browser — waiting for the agent</body>";
535
856
  await cdp.send("Page.enable").catch(() => void 0);
@@ -547,6 +868,20 @@ var BrowserRegistry = class {
547
868
  metadata: { seeded: true }
548
869
  });
549
870
  }
871
+ if (options.headless ?? cfg.headless) {
872
+ const birth = context.pages()[0];
873
+ if (birth && typeof birth.evaluate === "function") {
874
+ if (await birth.evaluate(() => {
875
+ const d = document.createElement("div");
876
+ d.style.cssText = "position:fixed;left:-100px;top:0;width:50px;height:50px;overflow:auto";
877
+ d.innerHTML = "<div style=\"width:200px;height:200px\"></div>";
878
+ document.body.appendChild(d);
879
+ const f = d.offsetHeight - d.clientHeight;
880
+ d.remove();
881
+ return f;
882
+ }).catch(() => null) === 0) throw new Error("glass scrollbar gate: bar footprint is 0 in a headless launch — --hide-scrollbars leaked past ignoreDefaultArgs (RCA 2026-10-28)");
883
+ }
884
+ }
550
885
  const brainPort = Number(process.env.DSI_MCP_PORT ?? cfg.brainPort);
551
886
  if (cfg.enabled) this.ensureReaper(cfg.idleMs);
552
887
  const entry = {
@@ -554,10 +889,14 @@ var BrowserRegistry = class {
554
889
  context,
555
890
  tabs,
556
891
  activeTargetId,
892
+ tabOrder: activeTargetId ? [activeTargetId] : [],
893
+ tabIds,
894
+ nextTabId,
557
895
  viewport: {
558
896
  width: Math.max(200, Math.min(1920, options.width ?? 730)),
559
897
  height: Math.max(120, Math.min(1080, options.height ?? 830))
560
898
  },
899
+ zoom: cfg.contentZoom,
561
900
  headless: options.headless ?? cfg.headless,
562
901
  port,
563
902
  pid,
@@ -570,9 +909,70 @@ var BrowserRegistry = class {
570
909
  mcpHealth: "up",
571
910
  mcpError: null,
572
911
  closed: false,
912
+ ownerSessionId: options.ownerSessionId ?? null,
913
+ ownBirths: /* @__PURE__ */ new Set(),
573
914
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
574
915
  };
575
916
  state().entries.set(id, entry);
917
+ if (typeof context.on === "function") context.on("page", (page) => {
918
+ (async () => {
919
+ for (let i = 0; i < 10; i += 1) {
920
+ await new Promise((r) => setTimeout(r, 300));
921
+ try {
922
+ const scdp = await context.newCDPSession(page);
923
+ const info = await scdp.send("Target.getTargetInfo").catch(() => null);
924
+ await scdp.detach().catch(() => void 0);
925
+ const tid = info?.targetInfo?.targetId;
926
+ if (!tid) continue;
927
+ if (!entry.tabOrder.includes(tid)) entry.tabOrder.push(tid);
928
+ mintTabId(entry, tid);
929
+ claimOwnerBirth(entry, tid);
930
+ if (entry.activeTargetId !== tid && !entry.closed && !entry.tabs.has(tid)) await this.activate(id, tid);
931
+ return;
932
+ } catch {}
933
+ }
934
+ })();
935
+ });
936
+ try {
937
+ const browser = context.browser?.();
938
+ if (browser && typeof browser.newBrowserCDPSession === "function") {
939
+ const bcdp = await browser.newBrowserCDPSession();
940
+ entry.browserCdp = bcdp;
941
+ await bcdp.send("Target.setDiscoverTargets", { discover: true }).catch(() => void 0);
942
+ bcdp.on("Target.targetCreated", (evt) => {
943
+ const tid = evt?.targetInfo?.targetId;
944
+ if (!tid || evt.targetInfo?.type !== "page") return;
945
+ if (entry.tabOrder.includes(tid) || entry.tabs.has(tid)) return;
946
+ (async () => {
947
+ for (let g = 0; g < 10; g += 1) {
948
+ await new Promise((r) => setTimeout(r, 300));
949
+ if (entry.closed) return;
950
+ if (entry.ownBirths?.has(tid)) return;
951
+ if (entry.tabOrder.includes(tid) || entry.tabs.has(tid)) return;
952
+ }
953
+ admitGuestBirth(entry, tid);
954
+ })();
955
+ });
956
+ }
957
+ } catch {}
958
+ if (!entry.headless) {
959
+ await this.applyPanelViewport(entry, {
960
+ cdp,
961
+ relay
962
+ }).catch(() => void 0);
963
+ const birthShot = await cdp.send("Page.captureScreenshot", {
964
+ format: "jpeg",
965
+ quality: cfg.screencastQuality
966
+ }).catch(() => null);
967
+ if (birthShot?.data) relay.reseed({
968
+ seq: 0,
969
+ jpeg: birthShot.data,
970
+ metadata: {
971
+ seeded: true,
972
+ birth: true
973
+ }
974
+ });
975
+ }
576
976
  if (options.spawnMcp ?? true) {
577
977
  if (process.env.DSI_MCP_IN_PROCESS === "1") {
578
978
  evictBrainPort(brainPort, id);
@@ -592,6 +992,7 @@ var BrowserRegistry = class {
592
992
  pid,
593
993
  profileDir,
594
994
  createdAt: entry.createdAt,
995
+ ownerSessionId: entry.ownerSessionId,
595
996
  mcp: entry.mcpChild ? "child" : entry.mcpInProcess ? "in-process" : "none",
596
997
  mcpHealth: entry.mcpHealth,
597
998
  mcpError: entry.mcpError
@@ -621,7 +1022,7 @@ var BrowserRegistry = class {
621
1022
  });
622
1023
  }
623
1024
  /** D8 owner-release. Idempotent: closing a closed id resolves cleanly. */
624
- async close(id) {
1025
+ async close(id, reason = "closed") {
625
1026
  const entry = state().entries.get(id);
626
1027
  if (!entry) return {
627
1028
  ok: true,
@@ -636,6 +1037,10 @@ var BrowserRegistry = class {
636
1037
  }
637
1038
  entry.closed = true;
638
1039
  state().closed.add(id);
1040
+ for (const cb of entry.deathListeners ?? []) try {
1041
+ cb(reason);
1042
+ } catch {}
1043
+ await entry.browserCdp?.detach().catch(() => void 0);
639
1044
  if (state().closed.size > 256) state().closed.delete(state().closed.values().next().value);
640
1045
  const errs = [];
641
1046
  if (entry.mcpInProcess) try {
@@ -731,24 +1136,244 @@ var BrowserRegistry = class {
731
1136
  await handle.cdp.send("Page.enable").catch(() => void 0);
732
1137
  const prevSeq = handle.relay.latestFrame()?.seq ?? -1;
733
1138
  await this.applyPanelViewport(e, handle);
734
- await this.seedAfterResize(handle, prevSeq).catch(() => void 0);
735
- }
736
- /** ADR-0041 D3: a resize must REPAINT, not wait for the next page
737
- * change — the screencast speaks only on change, so a static page
738
- * keeps the old-aspect frame letterboxed on the panel forever (the
739
- * operator's black bands, 2026-10-25). Bounded grace for a FRESH
740
- * frame (seq past prevSeq); silence answers with the deterministic
741
- * captureScreenshot seed — activate()'s first-frame idiom re-fired
742
- * for the resize path. Never hangs the verb past the grace. */
743
- async seedAfterResize(handle, prevSeq) {
744
- for (let i = 0; i < 10 && (handle.relay.latestFrame()?.seq ?? -1) === prevSeq; i += 1) await new Promise((r) => setTimeout(r, 100));
745
- if ((handle.relay.latestFrame()?.seq ?? -1) !== prevSeq) return;
1139
+ await this.repairSurfaceAfterResize(e, handle, prevSeq).catch(() => void 0);
1140
+ }
1141
+ /** Layout-scale zoom (ADR-0041 amendment, 2026-10-31): store the session
1142
+ * scale and re-apply the panel viewport — the emulation is box/zoom, so
1143
+ * the slider's LIVE effect rides the resize. Bad input (clampZoom null)
1144
+ * KEEPS the current zoom and returns silently: unreachable from the wire
1145
+ * (the route's clampZoom already answered 400) — documented keep. */
1146
+ async setZoom(id, zoom) {
1147
+ const e = this.peek(id);
1148
+ if (e.closed) throw new BrowserRegistryRefusal("CLOSED", "session is closing");
1149
+ const clamped = clampZoom(zoom);
1150
+ if (clamped === null) return;
1151
+ e.zoom = clamped;
1152
+ const handle = await this.handleOf(e);
1153
+ await this.applyPanelViewport(e, handle);
1154
+ }
1155
+ /** The window id, retried — Target.getWindowForTarget on a Playwright
1156
+ * page session races out on a COLD headed launch (measured in the
1157
+ * surface-lag test: both resizes skipped, the birth window kept its
1158
+ * size). Three bounded tries; the caller's race caps still apply. */
1159
+ async windowIdOf(anyCdp, race, targetId) {
1160
+ for (let i = 0; i < 3; i += 1) {
1161
+ const t = await race(anyCdp.send("Target.getWindowForTarget"), null);
1162
+ if (t && t.windowId) return t;
1163
+ const b = await race(anyCdp.send("Browser.getWindowForTarget", targetId ? { targetId } : {}), null);
1164
+ if (b && b.windowId) return b;
1165
+ await new Promise((r) => setTimeout(r, 500));
1166
+ }
1167
+ return null;
1168
+ }
1169
+ /** ADR-0041 D3 + the surface-lag repair (BUG 2026-10-26): a resize must
1170
+ * REPAINT at the REQUESTED size, not just emit SOME fresh frame. Two
1171
+ * failure shapes live here:
1172
+ * 1. the SILENT page — the screencast speaks only on change, so a
1173
+ * static page never repaints (the 2026-10-25 black bands);
1174
+ * 2. the STALE SURFACE — a headed off-screen window takes the new
1175
+ * RENDERER size but macOS never reallocates the compositor surface:
1176
+ * every frame (even captureScreenshot) keeps arriving at the OLD
1177
+ * height, and the panel letterboxes it (the operator's top/bottom
1178
+ * bands at every height grow). Measured: 53 frames over 6s all at
1179
+ * the old 832 while innerHeight was 919; captureScreenshot agreed
1180
+ * with the stale surface. A window-state BOUNCE (minimize ->
1181
+ * normal — invisible at -32000,-32000) forces the window server to
1182
+ * reallocate; frames then arrive at the requested size.
1183
+ * Ladder: grace for a frame whose JPEG dims MATCH the request ->
1184
+ * bounce (headed only) -> second grace -> deterministic
1185
+ * captureScreenshot reseed (reseed: a stale REAL frame may sit atop
1186
+ * the ring, and seed() refuses to cover one). Capped throughout —
1187
+ * nothing here may hang the verb (the 2.5s rule). */
1188
+ async repairSurfaceAfterResize(e, handle, prevSeq) {
1189
+ const want0 = {
1190
+ w: e.viewport.width,
1191
+ h: e.viewport.height
1192
+ };
1193
+ const lvw = layoutViewport(want0.w, want0.h, e.zoom ?? readBrowserConfig().contentZoom);
1194
+ const want = {
1195
+ w: lvw.width,
1196
+ h: lvw.height
1197
+ };
1198
+ const dimsOf = (f) => {
1199
+ if (!f) return null;
1200
+ const md = f.metadata;
1201
+ const mw = Number(md?.deviceWidth);
1202
+ const mh = Number(md?.deviceHeight);
1203
+ if (Number.isFinite(mw) && Number.isFinite(mh) && mw > 0 && mh > 0) return {
1204
+ w: mw,
1205
+ h: mh
1206
+ };
1207
+ const b = Buffer.from(f.jpeg, "base64");
1208
+ let i = 2;
1209
+ while (i < b.length - 9) {
1210
+ if (b[i] !== 255) {
1211
+ i += 1;
1212
+ continue;
1213
+ }
1214
+ const marker = b[i + 1];
1215
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
1216
+ h: b.readUInt16BE(i + 5),
1217
+ w: b.readUInt16BE(i + 7)
1218
+ };
1219
+ i += Math.max(2, b.readUInt16BE(i + 2));
1220
+ }
1221
+ return null;
1222
+ };
1223
+ const matched = () => {
1224
+ const f = handle.relay.latestFrame();
1225
+ if (!f || f.seq === prevSeq) return false;
1226
+ const d = dimsOf(f);
1227
+ return d !== null && Math.abs(d.w - want.w) < 3 && Math.abs(d.h - want.h) < 3;
1228
+ };
1229
+ for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
1230
+ if (matched()) return;
1231
+ if (e.headless) {
1232
+ const raceH = async (p, fb) => {
1233
+ const timeout = new Promise((res) => setTimeout(() => res(fb), 2500));
1234
+ return await Promise.race([p.catch(() => fb), timeout]);
1235
+ };
1236
+ if (handle.page) await raceH(handle.page.setViewportSize({
1237
+ width: want.w,
1238
+ height: want.h
1239
+ }).catch(() => void 0).then(() => null), null);
1240
+ await new Promise((r) => setTimeout(r, 400));
1241
+ const shotH = await raceH(handle.cdp.send("Page.captureScreenshot", {
1242
+ format: "jpeg",
1243
+ quality: readBrowserConfig().screencastQuality
1244
+ }).catch(() => null), null);
1245
+ if (shotH?.data) handle.relay.reseed({
1246
+ seq: 0,
1247
+ jpeg: shotH.data,
1248
+ metadata: {
1249
+ seeded: true,
1250
+ resize: true
1251
+ }
1252
+ });
1253
+ return;
1254
+ }
1255
+ if (!e.headless) {
1256
+ const race = async (p, fb) => {
1257
+ const timeout = new Promise((res) => setTimeout(() => res(fb), 2500));
1258
+ return await Promise.race([p.catch(() => fb), timeout]);
1259
+ };
1260
+ const anyCdp = handle.cdp;
1261
+ const win = await this.windowIdOf(anyCdp, race, e.activeTargetId ?? void 0);
1262
+ if (win?.windowId) {
1263
+ let cw = e.chrome?.w ?? 0;
1264
+ let ch = e.chrome?.h ?? 0;
1265
+ console.log("[surface] ladder start want", want.w + "x" + want.h, "learned chrome", cw + "/" + ch);
1266
+ for (let i = 0; i < 3; i += 1) {
1267
+ await race(anyCdp.send("Browser.setWindowBounds", {
1268
+ windowId: win.windowId,
1269
+ bounds: {
1270
+ width: Math.max(200, want.w + cw),
1271
+ height: Math.max(120, want.h + ch),
1272
+ windowState: "normal"
1273
+ }
1274
+ }), null);
1275
+ await new Promise((r) => setTimeout(r, 350));
1276
+ const shot = await race(anyCdp.send("Page.captureScreenshot", {
1277
+ format: "jpeg",
1278
+ quality: 60
1279
+ }), null);
1280
+ let d = shot?.data ? dimsOf({
1281
+ jpeg: shot.data,
1282
+ metadata: {}
1283
+ }) : null;
1284
+ if (!d) d = dimsOf(handle.relay.latestFrame());
1285
+ if (d && Math.abs(d.w - want.w) < 3 && Math.abs(d.h - want.h) < 3) {
1286
+ console.log("[surface] iter", i, "converged: capture", d.w + "x" + d.h, "chrome", cw + "/" + ch);
1287
+ e.chrome = {
1288
+ w: cw,
1289
+ h: ch
1290
+ };
1291
+ if (shot?.data) handle.relay.reseed({
1292
+ seq: 0,
1293
+ jpeg: shot.data,
1294
+ metadata: {
1295
+ seeded: true,
1296
+ resize: true
1297
+ }
1298
+ });
1299
+ return;
1300
+ }
1301
+ console.log("[surface] iter", i, "miss: capture", d ? d.w + "x" + d.h : "null", "at bounds", want.w + cw + "x" + (want.h + ch));
1302
+ if (!d) continue;
1303
+ const cur = await race(anyCdp.send("Browser.getWindowForTarget", { targetId: e.activeTargetId }), null);
1304
+ if (cur?.bounds?.width && cur?.bounds?.height) {
1305
+ cw = Math.max(0, cur.bounds.width - d.w);
1306
+ ch = Math.max(0, cur.bounds.height - d.h);
1307
+ } else {
1308
+ cw += Math.max(0, want.w - d.w);
1309
+ ch += Math.max(0, want.h - d.h);
1310
+ }
1311
+ }
1312
+ e.chrome = {
1313
+ w: cw,
1314
+ h: ch
1315
+ };
1316
+ await race(anyCdp.send("Browser.setWindowBounds", {
1317
+ windowId: win.windowId,
1318
+ bounds: { windowState: "minimized" }
1319
+ }), null);
1320
+ await race(anyCdp.send("Browser.setWindowBounds", {
1321
+ windowId: win.windowId,
1322
+ bounds: { windowState: "normal" }
1323
+ }), null);
1324
+ await race(anyCdp.send("Browser.setWindowBounds", {
1325
+ windowId: win.windowId,
1326
+ bounds: {
1327
+ width: Math.max(200, want.w + cw),
1328
+ height: Math.max(120, want.h + ch),
1329
+ windowState: "normal"
1330
+ }
1331
+ }), null);
1332
+ await new Promise((r) => setTimeout(r, 300));
1333
+ for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
1334
+ if (matched()) return;
1335
+ const parked = {
1336
+ width: Math.max(200, want.w + cw),
1337
+ height: Math.max(120, want.h + ch),
1338
+ windowState: "normal"
1339
+ };
1340
+ await race(anyCdp.send("Browser.setWindowBounds", {
1341
+ windowId: win.windowId,
1342
+ bounds: {
1343
+ ...parked,
1344
+ left: 10,
1345
+ top: 10
1346
+ }
1347
+ }), null);
1348
+ await new Promise((r) => setTimeout(r, 400));
1349
+ await race(anyCdp.send("Browser.setWindowBounds", {
1350
+ windowId: win.windowId,
1351
+ bounds: {
1352
+ ...parked,
1353
+ left: -32e3,
1354
+ top: -32e3
1355
+ }
1356
+ }), null);
1357
+ await new Promise((r) => setTimeout(r, 300));
1358
+ for (let i = 0; i < 10 && !matched(); i += 1) await new Promise((r) => setTimeout(r, 100));
1359
+ if (matched()) return;
1360
+ console.log("[surface] ladder exhausted: bounce+flash ran, stream frames still", (() => {
1361
+ const d = dimsOf(handle.relay.latestFrame());
1362
+ return d ? d.w + "x" + d.h : "unknown";
1363
+ })());
1364
+ }
1365
+ }
746
1366
  const cfg = readBrowserConfig();
747
1367
  const shot = await handle.cdp.send("Page.captureScreenshot", {
748
1368
  format: "jpeg",
749
1369
  quality: cfg.screencastQuality
750
1370
  }).catch(() => null);
751
- if (shot?.data) handle.relay.seed({
1371
+ const fd = shot?.data ? dimsOf({
1372
+ jpeg: shot.data,
1373
+ metadata: {}
1374
+ }) : null;
1375
+ console.log("[surface] final reseed capture:", fd ? fd.w + "x" + fd.h : "null", "want", want.w + "x" + want.h);
1376
+ if (shot?.data) handle.relay.reseed({
752
1377
  seq: 0,
753
1378
  jpeg: shot.data,
754
1379
  metadata: {
@@ -790,6 +1415,44 @@ var BrowserRegistry = class {
790
1415
  entryCount: hist.entries.length
791
1416
  };
792
1417
  }
1418
+ /** The FULL navigation history of one tab (long-press dropdown,
1419
+ * operator-approved 2026-10-28): the attached CDP session's
1420
+ * Page.getNavigationHistory, entries as { index, url, title } — index
1421
+ * is the LIST position the UI filters by, id is the CDP entryId the
1422
+ * travel verb needs. Read-only; the same handleOf refusals as navInfo. */
1423
+ async navEntries(id, targetId) {
1424
+ const e = this.peek(id);
1425
+ const h = await this.handleOf(e, targetId);
1426
+ await h.cdp.send("Page.enable").catch(() => void 0);
1427
+ const hist = await h.cdp.send("Page.getNavigationHistory", {});
1428
+ return {
1429
+ currentIndex: hist.currentIndex,
1430
+ entries: hist.entries.map((en, index) => ({
1431
+ index,
1432
+ url: en.url,
1433
+ title: en.title
1434
+ }))
1435
+ };
1436
+ }
1437
+ /** TRUE history travel (long-press dropdown, operator-approved
1438
+ * 2026-10-28): Page.navigateToHistoryEntry to the entry AT the list
1439
+ * position — never a fresh navigate, which would append a new entry
1440
+ * and desync the dropdown from Chrome's own back/forward list. A
1441
+ * position outside the list is an explicit refusal. */
1442
+ async historyTravel(id, index, targetId) {
1443
+ const e = this.peek(id);
1444
+ const h = await this.handleOf(e, targetId);
1445
+ await h.cdp.send("Page.enable").catch(() => void 0);
1446
+ const hist = await h.cdp.send("Page.getNavigationHistory", {});
1447
+ if (!Number.isInteger(index) || index < 0 || index >= hist.entries.length) throw new BrowserRegistryRefusal("NO_TARGET", "HISTORY_INDEX — no entry at index " + index + " of " + hist.entries.length);
1448
+ const loaded = this.waitLoad(h);
1449
+ await h.cdp.send("Page.navigateToHistoryEntry", { entryId: hist.entries[index].id });
1450
+ await loaded;
1451
+ return {
1452
+ currentIndex: index,
1453
+ entryCount: hist.entries.length
1454
+ };
1455
+ }
793
1456
  /** Reload the session's active page (ADR-0031 D4): Page.reload keeps
794
1457
  * the history entry, unlike re-navigating the current URL. */
795
1458
  async reload(id, targetId) {
@@ -901,12 +1564,15 @@ var BrowserRegistry = class {
901
1564
  waitLoad: true,
902
1565
  targetId
903
1566
  });
1567
+ const tabId = mintTabId(e, targetId);
904
1568
  if (opts?.activate ?? true) return {
905
1569
  ...await this.activate(id, targetId),
906
- targetId
1570
+ targetId,
1571
+ tabId
907
1572
  };
908
1573
  const h = e.tabs.get(targetId);
909
1574
  return {
1575
+ tabId,
910
1576
  targetId,
911
1577
  seq: h?.relay.latestFrame()?.seq ?? 0,
912
1578
  jpeg: h?.relay.latestFrame()?.jpeg ?? null,
@@ -930,10 +1596,23 @@ var BrowserRegistry = class {
930
1596
  async applyPanelViewport(e, handle) {
931
1597
  await handle.cdp.send("Page.enable").catch(() => void 0);
932
1598
  if (e.headless && handle.page) {
933
- await Promise.race([handle.page.setViewportSize({
934
- width: e.viewport.width,
935
- height: e.viewport.height
936
- }).catch(() => void 0), new Promise((res) => setTimeout(res, 2500))]);
1599
+ const lv = layoutViewport(e.viewport.width, e.viewport.height, e.zoom ?? readBrowserConfig().contentZoom);
1600
+ const resized = handle.page.setViewportSize({
1601
+ width: lv.width,
1602
+ height: lv.height
1603
+ }).catch(() => void 0);
1604
+ await Promise.race([resized, new Promise((res) => setTimeout(res, 2500))]);
1605
+ await Promise.race([resized, new Promise((res) => setTimeout(res, 5e3))]);
1606
+ try {
1607
+ const cdp = await handle.page.context().newCDPSession(handle.page);
1608
+ const dprEval = await Promise.race([handle.page.evaluate("devicePixelRatio").catch(() => 1), new Promise((res) => setTimeout(() => res(1), 500))]);
1609
+ await cdp.send("Emulation.setDeviceMetricsOverride", {
1610
+ width: lv.width,
1611
+ height: lv.height,
1612
+ deviceScaleFactor: typeof dprEval === "number" && dprEval > 0 ? dprEval : 1,
1613
+ mobile: false
1614
+ }).catch(() => void 0);
1615
+ } catch {}
937
1616
  return;
938
1617
  }
939
1618
  const race = async (p, fb) => await Promise.race([p.catch(() => fb), new Promise((res) => setTimeout(() => res(fb), 2500))]);
@@ -942,25 +1621,35 @@ var BrowserRegistry = class {
942
1621
  expression: "JSON.stringify({ oh: window.outerHeight - window.innerHeight, ow: window.outerWidth - window.innerWidth })",
943
1622
  returnByValue: true
944
1623
  }), null);
945
- let chromeH = 0, chromeW = 0;
1624
+ let chromeH = e.chrome?.h ?? 0;
1625
+ let chromeW = e.chrome?.w ?? 0;
946
1626
  try {
947
1627
  const d = JSON.parse(dims && dims.result && dims.result.value || "{}");
948
- chromeH = Number(d.oh) || 0;
949
- chromeW = Number(d.ow) || 0;
1628
+ const oh = Number(d.oh) || 0;
1629
+ const ow = Number(d.ow) || 0;
1630
+ if (oh > 0 || ow > 0) {
1631
+ chromeH = oh;
1632
+ chromeW = ow;
1633
+ }
950
1634
  } catch {}
951
1635
  const anyCdp2 = handle.cdp;
952
- const win = await race(anyCdp2.send("Target.getWindowForTarget"), null);
1636
+ const win = await this.windowIdOf(anyCdp2, race);
953
1637
  if (win && win.windowId) {
1638
+ const lv = layoutViewport(e.viewport.width, e.viewport.height, e.zoom ?? readBrowserConfig().contentZoom);
954
1639
  await race(anyCdp2.send("Browser.setWindowBounds", {
955
1640
  windowId: win.windowId,
956
1641
  bounds: {
957
- width: Math.max(200, e.viewport.width + chromeW),
958
- height: Math.max(120, e.viewport.height + chromeH),
1642
+ width: Math.max(200, lv.width + chromeW),
1643
+ height: Math.max(120, lv.height + chromeH),
959
1644
  windowState: "normal"
960
1645
  }
961
1646
  }), null);
1647
+ if (chromeW > 0 || chromeH > 0) e.chrome = {
1648
+ w: chromeW,
1649
+ h: chromeH
1650
+ };
962
1651
  const check = await race(anyCdp2.send("Browser.getWindowForTarget", { targetId: e.activeTargetId }), null);
963
- console.log("[window] resized ->", JSON.stringify(check && check.bounds ? check.bounds : check), "want", e.viewport.width + chromeW, "x", e.viewport.height + chromeH);
1652
+ console.log("[window] resized ->", JSON.stringify(check && check.bounds ? check.bounds : check), "want", lv.width + chromeW, "x", lv.height + chromeH);
964
1653
  } else console.log("[window] no windowId — resize skipped (raced out)");
965
1654
  await race(anyCdp2.send("Emulation.clearDeviceMetricsOverride"), null);
966
1655
  }
@@ -969,14 +1658,23 @@ var BrowserRegistry = class {
969
1658
  * to the first surviving tab (re-activated, first-frame contract
970
1659
  * re-fired). Closing the LAST tab is refused: the kill ladder owns
971
1660
  * session death (the trash is the operator-facing kill). */
972
- async closeTab(id, targetId) {
1661
+ async closeTab(id, ref) {
973
1662
  const e = this.peek(id);
974
1663
  if (e.closed) throw new BrowserRegistryRefusal("CLOSED", "session is closing");
1664
+ let targetId = ref;
1665
+ if (typeof ref === "number" || /^\d+$/.test(ref)) {
1666
+ const r = resolveTab(e, ref);
1667
+ if (!r.ok) throw new BrowserRegistryRefusal("NO_TARGET", r.error);
1668
+ targetId = r.targetId;
1669
+ }
975
1670
  if (e.tabs.size <= 1) throw new BrowserRegistryRefusal("NO_TARGET", "cannot close the last tab — use the trash to close the glass");
976
1671
  const victim = e.tabs.get(targetId);
977
1672
  if (!victim) throw new BrowserRegistryRefusal("NO_TARGET", "no such tab: " + targetId);
978
1673
  victim.relay.stop();
979
1674
  e.tabs.delete(targetId);
1675
+ retireTabId(e, targetId);
1676
+ const ordIdx = (e.tabOrder ?? []).indexOf(targetId);
1677
+ if (ordIdx !== -1) e.tabOrder.splice(ordIdx, 1);
980
1678
  await victim.cdp.send("Target.closeTarget", { targetId }).catch(() => void 0);
981
1679
  await victim.cdp.detach().catch(() => void 0);
982
1680
  if (e.activeTargetId === targetId) {
@@ -999,5 +1697,5 @@ var BrowserRegistry = class {
999
1697
  };
1000
1698
  var browserRegistry = new BrowserRegistry();
1001
1699
 
1002
- export { BrowserRegistryRefusal as B, browserRegistry as b };
1003
- //# sourceMappingURL=registry.js-CZNJZ0C-.js.map
1700
+ export { BrowserRegistryRefusal as B, browserRegistry as b, clampZoom as c };
1701
+ //# sourceMappingURL=registry.js-CWnRQ4vx.js.map