@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.
- package/.agents/skills/glass-dsi/SKILL.md +195 -0
- package/.agents/skills/glass-dsi/references/cdp-verification.md +132 -0
- package/.agents/skills/glass-dsi/scripts/_connect.mjs +81 -0
- package/.agents/skills/glass-dsi/scripts/glass-url.mjs +48 -0
- package/.agents/skills/glass-dsi/scripts/glass.mjs +84 -0
- package/.agents/skills/glass-url/SKILL.md +117 -0
- package/.agents/skills/glass-url/scripts/_args.mjs +49 -0
- package/.agents/skills/glass-url/scripts/_connect.mjs +99 -0
- package/.agents/skills/glass-url/scripts/glass-url.mjs +114 -0
- package/bin/dsi.mjs +23 -74
- package/bin/lib/zai-patch.mjs +115 -0
- package/build/client/_app/immutable/assets/0.Ch5VtsKp.css +2 -0
- package/build/client/_app/immutable/assets/2.D0QOh44D.css +1 -0
- package/build/client/_app/immutable/chunks/{DwcY-69G.js → 02Wfo2G3.js} +1 -1
- package/build/client/_app/immutable/chunks/0Tfhif1V.js +3 -0
- package/build/client/_app/immutable/chunks/{GKkjunMm.js → 0ruuBBbh.js} +2 -2
- package/build/client/_app/immutable/chunks/{DUCaFYlx.js → 4bHE1npv.js} +1 -1
- package/build/client/_app/immutable/chunks/{4cwyag_I.js → B4hDUAJl.js} +1 -1
- package/build/client/_app/immutable/chunks/{Cr2ck_ss.js → BIPXB-2I.js} +1 -1
- package/build/client/_app/immutable/chunks/{CeLBjOd9.js → BIgv-kQR.js} +1 -1
- package/build/client/_app/immutable/chunks/BK6k1nBs.js +1 -0
- package/build/client/_app/immutable/chunks/{SN3Efo-Q.js → BKwmKcDY.js} +1 -1
- package/build/client/_app/immutable/chunks/{CRFuOqJI.js → BORjuqef.js} +1 -1
- package/build/client/_app/immutable/chunks/{CH_bR_0j.js → BPmZB0-N.js} +1 -1
- package/build/client/_app/immutable/chunks/BROtno1Y.js +1 -0
- package/build/client/_app/immutable/chunks/{DEVxi5CK.js → BT5Iel00.js} +1 -1
- package/build/client/_app/immutable/chunks/{DdI16loP.js → BX3y_1Hh.js} +1 -1
- package/build/client/_app/immutable/chunks/{DuiFyTVb.js → Bea1gxLq.js} +1 -1
- package/build/client/_app/immutable/chunks/{D0uxHECr.js → BpkpgvVw.js} +1 -1
- package/build/client/_app/immutable/chunks/{CB55Rp7-.js → BreZS7EW.js} +1 -1
- package/build/client/_app/immutable/chunks/{BmDADyfH.js → BsS_byzk.js} +1 -1
- package/build/client/_app/immutable/chunks/{BWRXzjmQ.js → Bslg-dr5.js} +1 -1
- package/build/client/_app/immutable/chunks/{BkYT72a1.js → C-YczRN1.js} +1 -1
- package/build/client/_app/immutable/chunks/{DBKMIEwD.js → C2aW4lzo.js} +1 -1
- package/build/client/_app/immutable/chunks/{C4gs6zoI.js → C2qFXaz5.js} +1 -1
- package/build/client/_app/immutable/chunks/{BBd0nTHG.js → C5RyRpOh.js} +1 -1
- package/build/client/_app/immutable/chunks/{DJQr4efH.js → C5iHDVU_.js} +1 -1
- package/build/client/_app/immutable/chunks/{B4F5liKq.js → C7opVlLF.js} +1 -1
- package/build/client/_app/immutable/chunks/{DIxy2USu.js → CAuEkmZ4.js} +1 -1
- package/build/client/_app/immutable/chunks/{BO99LSSA.js → CIkiqgtA.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bpd0I3U7.js → CJesWaZn.js} +1 -1
- package/build/client/_app/immutable/chunks/{at6PQqQ4.js → CRsoS5Y9.js} +1 -1
- package/build/client/_app/immutable/chunks/CTWtsoJD.js +6 -0
- package/build/client/_app/immutable/chunks/CWk5gmU2.js +1 -0
- package/build/client/_app/immutable/chunks/{lF8CNVOA.js → CXkrluSK.js} +1 -1
- package/build/client/_app/immutable/chunks/{DmRY6FWm.js → C_8kFZ_X.js} +4 -4
- package/build/client/_app/immutable/chunks/{D6KenyiF.js → Ch7hUoAH.js} +1 -1
- package/build/client/_app/immutable/chunks/{CnzmIbjr.js → ChmmHPdM.js} +1 -1
- package/build/client/_app/immutable/chunks/{DfUgh5E_.js → CjajmcJs.js} +1 -1
- package/build/client/_app/immutable/chunks/{q5PRaRZk.js → Ck6GS6Vb.js} +1 -1
- package/build/client/_app/immutable/chunks/{eFcFJ9Ld.js → ClGoDr_f.js} +1 -1
- package/build/client/_app/immutable/chunks/{BQC6O_RV.js → ClqGHlec.js} +1 -1
- package/build/client/_app/immutable/chunks/{zypfUZYM.js → CnW6hje6.js} +1 -1
- package/build/client/_app/immutable/chunks/{OW5Q7tGb.js → CpI6VYCH.js} +1 -1
- package/build/client/_app/immutable/chunks/{BXKc4uuX.js → CygwwaiE.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bqwvv7Vf.js → CyhZtsZO.js} +35 -35
- package/build/client/_app/immutable/chunks/{BenkQ_4r.js → D-E2Noqq.js} +1 -1
- package/build/client/_app/immutable/chunks/{DuRZnRVw.js → D4Btzp_A.js} +1 -1
- package/build/client/_app/immutable/chunks/{CdaJvl_A.js → D4C3Jijv.js} +1 -1
- package/build/client/_app/immutable/chunks/D9jhmf3I.js +1 -0
- package/build/client/_app/immutable/chunks/{Cf0aMC4n.js → DCw0gAtv.js} +1 -1
- package/build/client/_app/immutable/chunks/{B6bTO8_7.js → DEW3KpiC.js} +1 -1
- package/build/client/_app/immutable/chunks/{O6xxeEm8.js → DYLFbKJe.js} +1 -1
- package/build/client/_app/immutable/chunks/{BsBBq-Qk.js → DYu7kgVa.js} +1 -1
- package/build/client/_app/immutable/chunks/Dfexc5qV.js +1 -0
- package/build/client/_app/immutable/chunks/{kzciT-3i.js → DhmUE8Ri.js} +1 -1
- package/build/client/_app/immutable/chunks/{DdJNsJBH.js → DjwX3rAC.js} +1 -1
- package/build/client/_app/immutable/chunks/{CajOZJDl.js → DlOfy8Sx.js} +1 -1
- package/build/client/_app/immutable/chunks/{CK5vhXvt.js → Do7Y1_MQ.js} +1 -1
- package/build/client/_app/immutable/chunks/{ap4GAVq-.js → Dq2XIb1m.js} +1 -1
- package/build/client/_app/immutable/chunks/{Czey8tDK.js → DuT_Qr5k.js} +1 -1
- package/build/client/_app/immutable/chunks/{4B6J_kM2.js → DyGggFea.js} +1 -1
- package/build/client/_app/immutable/chunks/{D0ChUnpi.js → Dym4i64U.js} +1 -1
- package/build/client/_app/immutable/chunks/FPsIAiN_.js +1 -0
- package/build/client/_app/immutable/chunks/{CevKuuic.js → GIczpP_D.js} +1 -1
- package/build/client/_app/immutable/chunks/{B2jrDnt8.js → LjOLSmU7.js} +1 -1
- package/build/client/_app/immutable/chunks/VtK73aN-.js +8 -0
- package/build/client/_app/immutable/chunks/{AcmOeh2U.js → ZqoQH_jm.js} +1 -1
- package/build/client/_app/immutable/chunks/{BlwWx85l.js → aXn6rE_7.js} +1 -1
- package/build/client/_app/immutable/chunks/{Czc4_W7v.js → dUOeoGwg.js} +1 -1
- package/build/client/_app/immutable/chunks/f50lp1rr.js +1 -0
- package/build/client/_app/immutable/chunks/{7FnrqGK9.js → lvLrv1cG.js} +1 -1
- package/build/client/_app/immutable/chunks/{CZoElI5B.js → pt3_jHwh.js} +1 -1
- package/build/client/_app/immutable/chunks/{Byu_teLA.js → q8irUX-p.js} +1 -1
- package/build/client/_app/immutable/chunks/{CnxoqrkQ.js → qmOgUo3c.js} +1 -1
- package/build/client/_app/immutable/chunks/vpNeypNM.js +1 -0
- package/build/client/_app/immutable/chunks/{DU8C5thd.js → xvTPLhcB.js} +1 -1
- package/build/client/_app/immutable/chunks/{DFBKiA2Z.js → zp7hrpsC.js} +1 -1
- package/build/client/_app/immutable/entry/app.D4oc_iez.js +2 -0
- package/build/client/_app/immutable/entry/start.Xkr2MCWg.js +1 -0
- package/build/client/_app/immutable/nodes/0.fsrqKU9i.js +1 -0
- package/build/client/_app/immutable/nodes/1.3F37GwcO.js +1 -0
- package/build/client/_app/immutable/nodes/2.DsWNNw0K.js +340 -0
- package/build/client/_app/version.json +1 -1
- package/build/handler.js +6 -6
- package/build/index.js +6 -6
- package/build/server/chunks/chunks/{app-config.svelte.js-BM4WbMFB.js → app-config.svelte.js-BXjvTmJu.js} +8 -3
- package/build/server/chunks/chunks/{config.js-vThvQeMn.js → config.js-r_jw_bQQ.js} +54 -2
- package/build/server/chunks/chunks/{db.js-DQm6CZ_r.js → db.js-f2PmBjyD.js} +2 -2
- package/build/server/chunks/chunks/{db2.js-DUhFxA3a.js → db2.js-DkwY64WO.js} +2 -2
- package/build/server/chunks/chunks/{dsh-connection.js-BgeraFA2.js → dsh-connection.js-BW07Aicl.js} +18 -10
- package/build/server/chunks/chunks/{dsh-events.js-BUkLR6ZV.js → dsh-events.js-BHf0lLZ5.js} +16 -3
- package/build/server/chunks/chunks/{git-probe.js-CLF37SRh.js → git-probe.js-CQqTV7XX.js} +3 -3
- package/build/server/chunks/chunks/{index-server.js-DmIEbDVz.js → index-server.js-CPkVoRxB.js} +2 -2
- package/build/server/chunks/chunks/{insight-config.js-Dp_2gB5J.js → insight-config.js-C5-Rhh5A.js} +19 -6
- package/build/server/chunks/chunks/{internal2.js-J3h-US64.js → internal2.js-DbC8VGqD.js} +4 -4
- package/build/server/chunks/chunks/ledger-stats.js-BWACyDPg.js +72 -0
- package/build/server/chunks/chunks/{registry.js-CZNJZ0C-.js → registry.js-CWnRQ4vx.js} +751 -53
- package/build/server/chunks/chunks/{registry2.js-D3Pr1Rv9.js → registry2.js-CFzpvj9c.js} +17 -1
- package/build/server/chunks/chunks/{server.js-B1bDXhHu.js → server.js-Dijo7DtV.js} +2 -2
- package/build/server/chunks/chunks/{settings-document.js-Gw1puaik.js → settings-document.js-DxKURfc_.js} +2 -2
- package/build/server/chunks/chunks/{state.js-8RBD56dL.js → state.js-DR4xiIVw.js} +5 -5
- package/build/server/chunks/chunks/{watcher.js-BVKnoQUw.js → watcher.js-DvlNn8X0.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-BjzdVwdY.js → _server.ts.js-DItDXV23.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-3kQCQuHT.js → _server.ts.js-Hor8QZb1.js} +8 -8
- package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-BUBTCLFn.js → _server.ts.js-CEvvsa8B.js} +8 -8
- package/build/server/chunks/entries/endpoints/api/browser/_id_/activate/{_server.ts.js-BdMJzENm.js → _server.ts.js-sXqf-okw.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/close/{_server.ts.js-B5z9-N8Y.js → _server.ts.js-Byj9ApJh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/closetab/{_server.ts.js-CnZHun5r.js → _server.ts.js-DOIsrzvg.js} +16 -9
- package/build/server/chunks/entries/endpoints/api/browser/_id_/element/{_server.ts.js-C1oM2Ihf.js → _server.ts.js-Cq3gmRfE.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/history/_server.ts.js-BGSLIV0t.js +61 -0
- package/build/server/chunks/entries/endpoints/api/browser/_id_/input/{_server.ts.js-DmbqGRMg.js → _server.ts.js-8slca75-.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/mcp/{_server.ts.js-Cj80T1yH.js → _server.ts.js-CpbdTYds.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/navigate/{_server.ts.js-6gi1UYTJ.js → _server.ts.js-Bls9LtLY.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/opentab/{_server.ts.js-Bhx_J7ot.js → _server.ts.js-DqNi_iW_.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/page/{_server.ts.js-DvQXNnX9.js → _server.ts.js-DuXWwo7i.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/stream/{_server.ts.js-MflkVWUn.js → _server.ts.js-DXfqBDEw.js} +38 -9
- package/build/server/chunks/entries/endpoints/api/browser/_id_/tabs/{_server.ts.js-CoUSlLcP.js → _server.ts.js-Bgnl8lYs.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/viewport/{_server.ts.js-DNk4EgjJ.js → _server.ts.js-FHmQVbrv.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/browser/_id_/zoom/_server.ts.js-BV4cBtSA.js +62 -0
- package/build/server/chunks/entries/endpoints/api/browser/{_server.ts.js-CECKap7O.js → _server.ts.js-4vgkwh7v.js} +9 -6
- package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-C8XDnDwe.js → _server.ts.js-C_9kwANR.js} +16 -6
- package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-BFmWshpv.js → _server.ts.js-h1OTHcL4.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/jobs/{_server.ts.js-DATWy5mQ.js → _server.ts.js-B6nih9Sd.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/jobs/follow/{_server.ts.js-CugOD1Zd.js → _server.ts.js-Cf17SVmD.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-DCupM4da.js → _server.ts.js-DjNwMbdS.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-Bh1btvr9.js → _server.ts.js-Cz5aaEhC.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-BFBy5O9Y.js +78 -0
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-WI9yH2iG.js → _server.ts.js-CgEdCo3T.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-bN9adUqz.js → _server.ts.js-Bo_aEnHh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-Bxlm966G.js → _server.ts.js-Bd2G_GaH.js} +5 -5
- 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
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-BzP30TXW.js → _server.ts.js-MhOGB66D.js} +7 -70
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-DvNkrTaY.js → _server.ts.js-MjPgXgGO.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-DXbCrefw.js → _server.ts.js-BUjDae5H.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-B7YRQhuJ.js → _server.ts.js-dTKd7Hsv.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-C_AEaLLJ.js → _server.ts.js-B82rvKoI.js} +5 -5
- 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
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-BX9FWJ6g.js → _server.ts.js-CL_vVv2p.js} +29 -6
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-CQ2Bmtu4.js → _server.ts.js-WS4W9Ly4.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-Ha7xjnKW.js → _server.ts.js-B72KGRGh.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-BOg6llHk.js → _server.ts.js-DK5xb2b5.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-DBqGALzZ.js → _server.ts.js-lhT87EQu.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-CvA8WDDb.js → _server.ts.js-CMhfhbkN.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/subagents/_server.ts.js-BLvcFsg6.js +72 -0
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-BfjNH40j.js → _server.ts.js-D_rw2Uq6.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-De_qumyr.js → _server.ts.js-DfXpMMpw.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-tFpJJHCb.js → _server.ts.js-2mJCJVxk.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-DVx5GGA_.js → _server.ts.js-BCx0Lzjf.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-DuKT-aZp.js → _server.ts.js-DECaSjwp.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-DsWFidFY.js → _server.ts.js-i_ciJzlb.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-C6j0adQs.js → _server.ts.js-BO1pScn7.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-BChAg4hm.js → _server.ts.js-Buhpf7WE.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-BPSHqbZN.js → _server.ts.js-DngUQzcT.js} +4 -4
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/output/{_server.ts.js-BNBxebgG.js → _server.ts.js-DLETP4yQ.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/reattach/{_server.ts.js-DDRJBGIr.js → _server.ts.js-uWl6oxPS.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/send/{_server.ts.js-CA0vc_Cm.js → _server.ts.js-DYNmXaAd.js} +9 -2
- package/build/server/chunks/entries/endpoints/api/terminal/_id_/stream/{_server.ts.js-D6L8Gd_f.js → _server.ts.js-C5m3Ktho.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-D5CbUe5k.js → _server.ts.js-DoTNvcOa.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-B-5YihUA.js → _server.ts.js-BzYOJSkI.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-DT5jRZFE.js → _server.ts.js-xcNesei9.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-DtW-utrd.js → _server.ts.js-DsJWYdxt.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-8AIAo9jX.js → _server.ts.js-CNCku9BR.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-CMw96vjY.js → _server.ts.js-nLsNjOxj.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-DKufa14_.js → _server.ts.js-urQk2-ds.js} +7 -7
- package/build/server/chunks/entries/{hooks.server.js-B-e4_ekh.js → hooks.server.js-CU2RE5vs.js} +3 -3
- package/build/server/chunks/entries/pages/{_error.svelte.js-DRPduJdP.js → _error.svelte.js-e018KpwE.js} +6 -6
- package/build/server/chunks/entries/pages/{_page.server.ts.js-7x6ZfGoB.js → _page.server.ts.js-BK3Y6eff.js} +3 -3
- package/build/server/chunks/entries/pages/{_page.svelte.js-Dek8Q1_v.js → _page.svelte.js-DQWBiRk1.js} +4185 -1734
- package/build/server/chunks/{handler-DUyvzXsb.js → handler-DsD7I1_u.js} +3 -3
- package/build/server/chunks/{index.js-BrP1SRzN.js → index.js-C_WAkwJs.js} +4 -4
- package/build/server/chunks/{manifest.js-C4z6aoYx.js → manifest.js-DUy52VaP.js} +85 -64
- package/build/server/chunks/nodes/{0.js-BuB3t8Br.js → 0.js-CuV3dXVD.js} +3 -3
- package/build/server/chunks/nodes/1.js-Dc5QC-NJ.js +9 -0
- package/build/server/chunks/nodes/{2.js-C-o9KF10.js → 2.js-PwcF4_kT.js} +10 -10
- package/package.json +2 -2
- package/skills/{dsi-kb-scenario → dsi-fc-scenario}/SKILL.md +11 -6
- package/template/.dsi/prompts.sqlite +0 -0
- package/template/.dsi/prompts.sqlite-shm +0 -0
- package/template/.dsi/prompts.sqlite-wal +0 -0
- package/template/.dsi/settings.yaml +7 -2
- package/build/client/_app/immutable/assets/0.cswkTiBk.css +0 -2
- package/build/client/_app/immutable/assets/2.BPprd30B.css +0 -1
- package/build/client/_app/immutable/chunks/BHDqH9nE.js +0 -1
- package/build/client/_app/immutable/chunks/C7Sg_49n.js +0 -1
- package/build/client/_app/immutable/chunks/CAneZX4t.js +0 -8
- package/build/client/_app/immutable/chunks/D-Rolhzs.js +0 -1
- package/build/client/_app/immutable/chunks/DQXo7qdJ.js +0 -1
- package/build/client/_app/immutable/chunks/DY0MQ1mO.js +0 -1
- package/build/client/_app/immutable/chunks/K3xyLTzZ.js +0 -1
- package/build/client/_app/immutable/chunks/NcOjFWr3.js +0 -6
- package/build/client/_app/immutable/chunks/cHj8EuyS.js +0 -3
- package/build/client/_app/immutable/chunks/o7DVZODA.js +0 -1
- package/build/client/_app/immutable/chunks/pdYhlwhf.js +0 -1
- package/build/client/_app/immutable/entry/app.DYNedUxy.js +0 -2
- package/build/client/_app/immutable/entry/start.BzyrBssE.js +0 -1
- package/build/client/_app/immutable/nodes/0.DQYP6BCS.js +0 -1
- package/build/client/_app/immutable/nodes/1.CHB5qqQ9.js +0 -1
- package/build/client/_app/immutable/nodes/2.C6No1310.js +0 -340
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-Cb8SBWVw.js +0 -46
- package/build/server/chunks/nodes/1.js-CW4V8ni8.js +0 -9
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: glass-dsi
|
|
3
|
+
description: Use when asked to test/verify work "live", "on the glass", "headed", or on the real DSI surface — drive the operator's own Glass Browser over its MCP tools (mcp__glass-browser__browser_*), opening a glass panel if none exists or adding a focused tab when one does. Reserved keyword /glass-dsi (legacy alias /dsi-glass still matches).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# glass-dsi — verify work on the operator's own Glass Browser
|
|
7
|
+
|
|
8
|
+
**Dispatch rule (2026-10-30, the Tab-Juggling RCA):** before any glass work,
|
|
9
|
+
ask — is the surface I must drive the DSI workspace, or some other URL?
|
|
10
|
+
DSI workspace → this skill (the driver hard-requires the DSI tab on :5175).
|
|
11
|
+
Any other URL → the `glass-url` skill; it opens a NEW tab and NEVER touches
|
|
12
|
+
the DSI tab. Do NOT navigate the DSI tab to an external URL and do NOT
|
|
13
|
+
re-run `open` to "recover" — that dance produced a useless DSI tab, a
|
|
14
|
+
strike from the driver, and three tabs where one was needed.
|
|
15
|
+
|
|
16
|
+
Companion KB: `dev/feature-contract-scenario/glass-browser-verification.md`
|
|
17
|
+
(the why and the wire: CDP, the :9515 in-process brain, the three doors).
|
|
18
|
+
This skill is the DO-path.
|
|
19
|
+
Reference (raw-CDP DO-path when the MCP tools cannot reach the glass —
|
|
20
|
+
orphaned glasses, stack-trace instrumentation, no-nesting rule):
|
|
21
|
+
`references/cdp-verification.md` in this skill folder. The one rule above all:
|
|
22
|
+
|
|
23
|
+
**The hands are the browser.** `mcp__glass-browser__browser_*` tools drive
|
|
24
|
+
the SAME headed Chromium the operator's floor panel mirrors (CDP via the
|
|
25
|
+
in-process brain). Never launch your own Playwright browser — that
|
|
26
|
+
verifies a different browser.
|
|
27
|
+
|
|
28
|
+
## Step 0 — is DSI even running? (and the circularity rule)
|
|
29
|
+
|
|
30
|
+
Port check — `lsof` on macOS; on Linux it is often not installed, so
|
|
31
|
+
fall back to `ss` (the dsi-ports skill is the full ledger):
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
# macOS (lsof ships with the OS)
|
|
35
|
+
lsof -nP -iTCP:5175 -sTCP:LISTEN # vite dev server
|
|
36
|
+
lsof -nP -iTCP:9515 -sTCP:LISTEN # the in-process MCP brain
|
|
37
|
+
# Linux fallback (lsof frequently absent)
|
|
38
|
+
ss -ltnp 'sport = :5175'; ss -ltnp 'sport = :9515'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
No 5175 ⇒ the floor is not up — ask the operator (or run
|
|
42
|
+
`dsi dsh --sync --dev --playwright-mcp`) before anything else.
|
|
43
|
+
|
|
44
|
+
**The circularity rule (learned 2026-10-27):** the brain on :9515 exists
|
|
45
|
+
only while a glass session exists, and the only browser hands you have
|
|
46
|
+
are the `mcp__glass-browser__browser_*` tools — which are dead until the
|
|
47
|
+
brain exists. `fetch failed` from the MCP tools means "no glass
|
|
48
|
+
session", NOT "DSI is down" — do not treat it as a floor outage and do
|
|
49
|
+
not go fix ports.
|
|
50
|
+
|
|
51
|
+
## Step 1 — the Glass Browser exists? (THE definitive procedure)
|
|
52
|
+
|
|
53
|
+
**Hard architecture facts (learned the hard way, 2026-10-27 — do not
|
|
54
|
+
re-litigate them):**
|
|
55
|
+
|
|
56
|
+
- The glass Chrome is launched `headless: true` and parked at
|
|
57
|
+
`--window-position=-32000,-32000` BY DESIGN (insight-config.ts,
|
|
58
|
+
`browser.headless` default; ADR-0041). The operator's floor PANEL is
|
|
59
|
+
the browser's face — a CDP screencast over SSE. There is no OS window
|
|
60
|
+
to look for; never "fix" this.
|
|
61
|
+
- The floor panel mount is CLIENT state only (`openBrowserPanel` →
|
|
62
|
+
in-memory floor store + localStorage blob). There is NO API that mounts
|
|
63
|
+
a panel. A session you `POST /api/browser` yourself is an ORPHAN: it
|
|
64
|
+
streams fine, but no panel can ever adopt it (the resolver persists
|
|
65
|
+
only ids IT opened). Do not open sessions yourself expecting the floor
|
|
66
|
+
to show them.
|
|
67
|
+
|
|
68
|
+
**The one correct open flow (verified end-to-end 2026-10-27):**
|
|
69
|
+
|
|
70
|
+
1. **No glass, or your tools fail with `fetch failed`?** Ask the
|
|
71
|
+
OPERATOR to click the Browser chip (`OpenBrowserButton`) in the
|
|
72
|
+
conversation header. Only their click mounts the panel into
|
|
73
|
+
PanelsZoom and only the panel's own resolver opens + persists the
|
|
74
|
+
session. This is the one gesture the agent cannot simulate — say so
|
|
75
|
+
plainly and wait.
|
|
76
|
+
2. **Catch the session**: the click's panel opens the NEWEST entry in
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
curl -s http://127.0.0.1:5175/api/browser # → { sessions: [...] }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The brain re-homes to the newest glass automatically — poll this list
|
|
83
|
+
(a couple of seconds) until the new id appears with
|
|
84
|
+
`mcpHealth: "up"` and :9515 listens. Then the `browser_*` tools
|
|
85
|
+
drive exactly that session.
|
|
86
|
+
3. **Drive + verify the mirror**: `browser_navigate` to the target, and
|
|
87
|
+
confirm the panel is really painting by probing the SSE:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
curl -s -N --max-time 4 'http://127.0.0.1:5175/api/browser/<id>/stream' | head -c 60
|
|
91
|
+
# expect: event: frame\ndata: {"seq":N,"jpeg":"/9j/...
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Rising `seq` = the operator's panel is receiving live frames. Finish
|
|
95
|
+
by asking the operator to confirm they SEE the page — the mirror is
|
|
96
|
+
the surface they judge you by.
|
|
97
|
+
- **Glass already open with tabs** — `browser_tabs` with
|
|
98
|
+
`{ "action": "new", "url": <target> }` adds a tab LAST and focuses it,
|
|
99
|
+
then navigate/verify in that tab. Never reuse an operator's existing
|
|
100
|
+
tab state; never close their tabs.
|
|
101
|
+
|
|
102
|
+
## Step 2 — drive the verification
|
|
103
|
+
|
|
104
|
+
1. **Reach the surface** — navigate to the target (usually
|
|
105
|
+
`http://127.0.0.1:5175`). Seed state through the page's OWN
|
|
106
|
+
persistence (localStorage blobs like `dsi-panels`) and reload, so
|
|
107
|
+
what mounts is the production restore path, not a fixture.
|
|
108
|
+
2. **Instrument BEFORE acting** — `browser_evaluate`: stamp the DOM
|
|
109
|
+
nodes you will judge (`el.__probe = Math.random()`), wrap
|
|
110
|
+
`window.fetch` with a counting shim, arm a MutationObserver. A
|
|
111
|
+
counter armed after the gesture proves nothing.
|
|
112
|
+
3. **Reproduce the REAL gesture** — `browser_run_code_unsafe` with
|
|
113
|
+
`page.mouse` (down → stepped moves with waits → up), or
|
|
114
|
+
`browser_click`. Synthetic dispatchEvent is not the operator's
|
|
115
|
+
gesture; prefer real input where input is what's being tested.
|
|
116
|
+
4. **Read the evidence** — `browser_evaluate`: node identity (did the
|
|
117
|
+
stamped probe survive?), counter deltas, innerHTML before/after.
|
|
118
|
+
Before/after pairs are the verification; adjectives are not.
|
|
119
|
+
5. **Gate every read on a control** — assert the gesture actually moved
|
|
120
|
+
what it should (e.g. the panel WIDTH changed) before interpreting
|
|
121
|
+
other counters. A drag that moved nothing trivially "passes".
|
|
122
|
+
|
|
123
|
+
## Failure branches (all observed 2026-10-27 — this is the baseline)
|
|
124
|
+
|
|
125
|
+
- **Geometry lies.** `boundingBox` center can land above the viewport
|
|
126
|
+
fold; the drag moves nothing and every counter reads clean. Fix the
|
|
127
|
+
coordinates (use `g.y + 200` inside a tall gutter) and re-run.
|
|
128
|
+
- **The wrong twin.** In-panel and floor gutters share
|
|
129
|
+
`data-testid=panel-gutter-0`. Scope selectors
|
|
130
|
+
(`[data-testid=panels-row] > [data-testid=panel-gutter-0]` = floor)
|
|
131
|
+
or you verify the wrong machine.
|
|
132
|
+
- **Dev-server HMR reloads mid-test** — the page navigates under you,
|
|
133
|
+
stamps and shims vanish, counters reset. Detect it (readyState /
|
|
134
|
+
element gone) and re-arm; never report numbers from a destroyed
|
|
135
|
+
context.
|
|
136
|
+
- **Blob drift** — the app saves panel-prefs on settle; your seeded
|
|
137
|
+
state can be overwritten by an earlier interaction. Re-seed + reload
|
|
138
|
+
when the surface looks wrong.
|
|
139
|
+
- **The in-memory registry** — the browser-session registry lives in the
|
|
140
|
+
dev server's RAM. A vite restart (crash, HMR of server modules, config
|
|
141
|
+
edit) silently empties `GET /api/browser` and kills every Chrome;
|
|
142
|
+
"down"/vanished sessions you did not close mean this happened. Re-run
|
|
143
|
+
Step 1 (operator clicks the chip again).
|
|
144
|
+
- **`DELETE /api/browser/<id>` is unreliable** — observed 500 twice
|
|
145
|
+
(2026-10-27) while the session stayed registered. If you must clean a
|
|
146
|
+
dead entry, verify with `GET /api/browser` afterwards; do not assume
|
|
147
|
+
the DELETE succeeded.
|
|
148
|
+
|
|
149
|
+
## Report
|
|
150
|
+
|
|
151
|
+
Numbers before/after, the gesture that produced them, and the gates that
|
|
152
|
+
passed. Label anything you seeded as seeded. If a step could not run,
|
|
153
|
+
say so — a partial verification is a claim about what RAN, not about
|
|
154
|
+
the feature.
|
|
155
|
+
## Tab-ID grammar (2026-10-28 — glass-tab-ids wave)
|
|
156
|
+
|
|
157
|
+
Glass prompts and the panel omnibox share ONE parser: src/lib/browser/tab-grammar.ts (`parseTabCommand`, discriminated union) — the both-doors rule; they can never disagree about what a command means. Grammar:
|
|
158
|
+
|
|
159
|
+
| Input | Meaning |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `glass <url>` | open a new tab to url |
|
|
162
|
+
| `glass <text>` | open a new tab to a web search of text |
|
|
163
|
+
| `reuse tab:N [url]` | focus tab N, optionally navigate |
|
|
164
|
+
| `change [to] tab:N` / `tab:N` | focus tab N |
|
|
165
|
+
| `close tab:N` / `close active` | close that tab / the active tab |
|
|
166
|
+
| `active` | report the focused tab (id + URL) |
|
|
167
|
+
| anything else | chrome fallback — navigate or web search on the active tab |
|
|
168
|
+
|
|
169
|
+
`Alt+N` on the panel (not a grammar input) focuses the Nth tab by STRIP POSITION before the CDP relay — Alt+0 is the last tab; empty slot is a no-op and the combo never reaches the glass page.
|
|
170
|
+
|
|
171
|
+
`tab:N` is the registry-owned ordinal rendered on the strip badge (data-testid=browser-tab-ordinal) — the badge IS the prompt ID. Focus polish 2026-10-28: the badge now READS `t{index}:{id}` (the 1-based strip position, the same index Alt+N dispatches on), while PROMPTS still use the `tab:{id}` grammar — the registry is unchanged. Ordinals are never reused after close: a retired/unknown tab:N is an explicit error (404 NO_TARGET), never a silent nearest-match. When verifying live, read the number off the badge and address the tab by it.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## Fast path — the driver script (2026-10-29)
|
|
175
|
+
|
|
176
|
+
The whole discovery dance above is codified in ONE script. Run it FIRST;
|
|
177
|
+
fall back to the manual steps only when it fails:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
# from the DSI repo root (playwright-core resolves there)
|
|
181
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs open # resolve port, connect, reuse-or-create the DSI tab
|
|
182
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs type '?adr' # real keystrokes into the composer
|
|
183
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs strip # rows / activeIdx / ariaSel / tipCount verdict
|
|
184
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs shot tmp/glass.png
|
|
185
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs eval '1+1' # arbitrary page.evaluate
|
|
186
|
+
node .agents/skills/dsi-glass/scripts/glass.mjs pages
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
It caches NOTHING: every run re-resolves the session from
|
|
190
|
+
`GET /api/browser` (rule 3 — a hardcoded port verifies a ghost). On
|
|
191
|
+
failure it prints the manual fallback (operator clicks the Browser chip).
|
|
192
|
+
Complex gestures (mouse drags, multi-step keyboards, instrumentation
|
|
193
|
+
shims) still go through playwright-core by hand per §2–§4 — the script
|
|
194
|
+
covers the connect + common verbs, not every probe.
|
|
195
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Reference — Driving a Glass Over Raw CDP (the orphaned-glass DO-path)
|
|
2
|
+
|
|
3
|
+
Companion to SKILL.md. Read when the `mcp__glass-browser__browser_*`
|
|
4
|
+
tools cannot reach the glass you need (brain re-homed, preset entry
|
|
5
|
+
stale) or when you need evidence the MCP tools cannot produce (stack
|
|
6
|
+
traces, per-frame scroll curves, wire-call counts). The KB companion
|
|
7
|
+
(`dev/feature-contract-scenario/glass-browser-verification.md`,
|
|
8
|
+
"2026-10-28 additions II") carries the WHY; this file is the HOW, every
|
|
9
|
+
pattern battle-tested 2026-10-28 on the operator's own glass (port 9400,
|
|
10
|
+
three session ids, one investigation — the floor-steal RCA).
|
|
11
|
+
|
|
12
|
+
## 0. The rules that outrank every pattern
|
|
13
|
+
|
|
14
|
+
1. **NO NESTING.** Never open the host DSI inside a glass tab to "drive
|
|
15
|
+
the GUI". The host GUI's gestures belong to the OPERATOR; you verify
|
|
16
|
+
effects through the glass CDP and ask the operator to click. (Operator
|
|
17
|
+
correction 2026-10-28: "do not nest DSI".)
|
|
18
|
+
2. **A re-homed glass is not dead.** `mcpHealth: "down", mcpError:
|
|
19
|
+
"brain re-homed to a newer glass"` means the MCP tools left — the
|
|
20
|
+
chromium and its CDP port are still fully alive and still may be the
|
|
21
|
+
browser the operator's panel mirrors.
|
|
22
|
+
3. **Re-resolve the port every session.** `GET /api/browser` rows carry
|
|
23
|
+
`port`; the registry cycles whenever the operator re-chips. A
|
|
24
|
+
hardcoded port verifies a ghost.
|
|
25
|
+
4. **Instrument BEFORE the gesture.** A counter armed after the click
|
|
26
|
+
proves nothing.
|
|
27
|
+
|
|
28
|
+
## 1. Resolve the target
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
curl -s http://127.0.0.1:5175/api/browser
|
|
32
|
+
# → {"sessions":[{"id":"…","port":9400,"mcpHealth":"…"}]}
|
|
33
|
+
curl -s http://127.0.0.1:<port>/json/list
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Pick the DSI-under-test page: `type === "page"`, URL contains
|
|
37
|
+
`127.0.0.1:5175`. Its `title` is the FOCUSED SESSION's name — DSI sets
|
|
38
|
+
`document.title` to it, so the title flipping IS a focus probe. Ignore
|
|
39
|
+
`browser_ui` rows (Omnibox Popup) and the `data:text/html` placeholder.
|
|
40
|
+
|
|
41
|
+
## 2. Connect (playwright-core over CDP — real input, real pages)
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { chromium } from 'playwright-core';
|
|
45
|
+
const browser = await chromium.connectOverCDP('http://127.0.0.1:' + port);
|
|
46
|
+
const page = browser.contexts()[0].pages().find(p => p.url().includes('5175'));
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This gives real `locator.click()` gestures (pointerdown included — the
|
|
50
|
+
activation verb) and `page.evaluate` into the live page. Raw-CDP
|
|
51
|
+
alternatives for the same doors: `Runtime.evaluate` (+`userGesture:
|
|
52
|
+
true`), `Input.dispatchMouseEvent/KeyEvent`, `Page.captureScreenshot`.
|
|
53
|
+
|
|
54
|
+
## 3. Arm the instrumentation (one `page.evaluate`, before acting)
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
await page.evaluate(() => {
|
|
58
|
+
window.__ev = []; window.__sl = [];
|
|
59
|
+
const vp = document.querySelector('[data-testid=panels-viewport]');
|
|
60
|
+
// (a) rAF scroll sampler — CDP round-trips miss the smooth-scroll curve
|
|
61
|
+
let last = vp.scrollLeft;
|
|
62
|
+
const sample = () => { const s = vp.scrollLeft;
|
|
63
|
+
if (s !== last) { last = s; window.__sl.push({ t: Math.round(performance.now()), sl: Math.round(s * 10) / 10 }); }
|
|
64
|
+
requestAnimationFrame(sample); };
|
|
65
|
+
requestAnimationFrame(sample);
|
|
66
|
+
const proto = Element.prototype;
|
|
67
|
+
// (b) scroll watches with STACKS — the stack frame names the guilty component
|
|
68
|
+
if (!proto.__watched) { proto.__watched = true;
|
|
69
|
+
const siv = proto.scrollIntoView, sto = proto.scrollTo;
|
|
70
|
+
proto.scrollIntoView = function (...a) {
|
|
71
|
+
if (this.closest('[data-testid=panels-viewport]'))
|
|
72
|
+
window.__ev.push({ t: Math.round(performance.now()), kind: 'scrollIntoView',
|
|
73
|
+
d: (this.getAttribute?.('data-testid') ?? this.tagName),
|
|
74
|
+
stack: new Error().stack.split('\n')[2]?.trim() });
|
|
75
|
+
return siv.apply(this, a); };
|
|
76
|
+
proto.scrollTo = function (...a) {
|
|
77
|
+
if (this.matches?.('[data-testid=panels-viewport]'))
|
|
78
|
+
window.__ev.push({ t: Math.round(performance.now()), kind: 'floor.scrollTo', d: JSON.stringify(a[0]) });
|
|
79
|
+
return sto.apply(this, a); }; }
|
|
80
|
+
// (c) fetch shim — negative evidence (zero POSTs) kills hypotheses
|
|
81
|
+
if (!window.__fw) { window.__fw = true; const of_ = window.fetch;
|
|
82
|
+
window.fetch = function (...a) {
|
|
83
|
+
const u = String(a[0]);
|
|
84
|
+
if (u.includes('/viewport') || u.includes('/activate') || u.includes('/tabs'))
|
|
85
|
+
window.__ev.push({ t: Math.round(performance.now()), kind: 'fetch', d: u.split('/').pop() });
|
|
86
|
+
return of_.apply(this, a); }; }
|
|
87
|
+
// (d) selection timestamps
|
|
88
|
+
new MutationObserver(() => window.__ev.push({ t: Math.round(performance.now()), kind: 'selection' }))
|
|
89
|
+
.observe(document.querySelector('[data-testid=panels-row]'), { attributes: true, attributeFilter: ['class'], subtree: true });
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Notes: arm once (the `__watched`/`__fw` guards) — HMR reloads wipe
|
|
94
|
+
everything and the guards re-arm cleanly; the stack slice `[2]` is the
|
|
95
|
+
first APP frame (skip the wrapper).
|
|
96
|
+
|
|
97
|
+
## 4. Gesture, then read the verdict
|
|
98
|
+
|
|
99
|
+
Perform the REAL gesture (`locator.click()` on the panel header), wait
|
|
100
|
+
OUT past at least one poll cadence (the tabs poll is 2000 ms — a 2s wait
|
|
101
|
+
verifies nothing), then:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const out = await page.evaluate(() => ({
|
|
105
|
+
sl: document.querySelector('[data-testid=panels-viewport]').scrollLeft,
|
|
106
|
+
sel: document.querySelector('.panels-row [data-testid=panel-column].selected')
|
|
107
|
+
?.getBoundingClientRect().toJSON(),
|
|
108
|
+
vp: document.querySelector('[data-testid=panels-viewport]').getBoundingClientRect().toJSON(),
|
|
109
|
+
ev: window.__ev, samples: window.__sl }));
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Verdict = NUMBERS: selected rect fully inside viewport rect? scroll trace
|
|
113
|
+
monotonic (reveal settles) or snapped back (a steal)? which stack frames
|
|
114
|
+
appear? Report before/after pairs and the gates passed; adjectives are not
|
|
115
|
+
verification.
|
|
116
|
+
|
|
117
|
+
## 5. Geometry facts that bite
|
|
118
|
+
|
|
119
|
+
- The floor viewport's left edge is NOT page x=0 (the sidebar rail sits
|
|
120
|
+
left of it) — compare column rects against `panels-viewport`'s OWN
|
|
121
|
+
rect, never against the page.
|
|
122
|
+
- `getBoundingClientRect` is transform-true (zoom included); `clientWidth`
|
|
123
|
+
is layout px. At floor zoom ≠ 1 the two disagree — use the rect.
|
|
124
|
+
- A 12px glass resize you did not cause is usually the mirror's
|
|
125
|
+
viewport-sync, not your gesture — check the fetch shim before claiming it.
|
|
126
|
+
|
|
127
|
+
## 6. Worked example
|
|
128
|
+
|
|
129
|
+
The full live trace that convicted the tab-strip floor steal (instrument
|
|
130
|
+
→ gesture → stack frame → fix → re-trace) is in
|
|
131
|
+
`dev/kb/rca/2026-10-28 - RCA — The Tab Chip That Rearranged the Room
|
|
132
|
+
(A Strip's Scroll Walked Into the Floor).md`.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* _connect.mjs — shared glass connection for the glass-url / glass-dsi drivers.
|
|
4
|
+
*
|
|
5
|
+
* EXPORTS (do not guess — this header is the contract, keep it in sync):
|
|
6
|
+
* die(msg) — print a JSON error and exit 1.
|
|
7
|
+
* identityFrom(argv) — caller identity, first-wins: --owner-session
|
|
8
|
+
* flag > GLASS_OWNER_SESSION > DSH_SESSION_ID
|
|
9
|
+
* (the harness built-in is CONDITIONAL —
|
|
10
|
+
* dsh-shell-env index.js:89 injects it only
|
|
11
|
+
* when the execution carries an agent binding,
|
|
12
|
+
* RCA 2026-10-30 "the session id is gone" —
|
|
13
|
+
* so explicit beats inherited).
|
|
14
|
+
* pickSession(sessions, envId)
|
|
15
|
+
* — PURE D7 resolver (unit-tested): match the
|
|
16
|
+
* caller's identity to ownerSessionId. NEVER
|
|
17
|
+
* registry index (ADR-0045 amendment 3).
|
|
18
|
+
* Returns { session } | { session:null, error }.
|
|
19
|
+
* connect() — GET /api/browser (the ONLY source of truth,
|
|
20
|
+
* ADR-0030) → pickSession → CDP connect →
|
|
21
|
+
* { session, browser, ctx }. Dies LOUDLY on
|
|
22
|
+
* ambiguity; degraded single-session runs print
|
|
23
|
+
* a warning on stderr and proceed.
|
|
24
|
+
* pick(ctx, substr) — page whose URL contains substr; NEVER
|
|
25
|
+
* positional "newest" among foreign tabs (D2).
|
|
26
|
+
*/
|
|
27
|
+
import { chromium } from 'playwright-core';
|
|
28
|
+
|
|
29
|
+
const DSI = 'http://127.0.0.1:5175';
|
|
30
|
+
|
|
31
|
+
export function die(msg) {
|
|
32
|
+
console.error(JSON.stringify({ error: msg, fallback: 'SKILL.md Step 1 — ask the OPERATOR to click the Browser chip IN THE RIGHT CONVERSATION, then catch the session via GET /api/browser. If DSH_SESSION_ID is absent from env, pass --owner-session <id> (the session id you already know from context).' }));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function identityFrom(argv) {
|
|
37
|
+
const flag = argv.indexOf('--owner-session');
|
|
38
|
+
if (flag !== -1 && argv[flag + 1]) return argv[flag + 1];
|
|
39
|
+
if (process.env.GLASS_OWNER_SESSION) return process.env.GLASS_OWNER_SESSION;
|
|
40
|
+
if (process.env.DSH_SESSION_ID) return process.env.DSH_SESSION_ID;
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function pickSession(sessions, envId) {
|
|
45
|
+
const list = sessions ?? [];
|
|
46
|
+
if (envId) {
|
|
47
|
+
const mine = list.filter((s) => (s.ownerSessionId ?? null) === envId);
|
|
48
|
+
if (mine.length === 1) return { session: mine[0], degraded: false };
|
|
49
|
+
if (mine.length > 1) {
|
|
50
|
+
const newest = [...mine].sort((a, b) => String(a.createdAt ?? '').localeCompare(String(b.createdAt ?? ''))).at(-1);
|
|
51
|
+
return { session: newest, degraded: false, note: 'multiple glasses owned by ' + envId + ' — newest wins' };
|
|
52
|
+
}
|
|
53
|
+
if (list.length === 1 && (list[0].ownerSessionId ?? null) === null) {
|
|
54
|
+
return { session: list[0], degraded: true, note: 'legacy session without ownerSessionId — degraded match' };
|
|
55
|
+
}
|
|
56
|
+
return { session: null, error: 'no glass owned by ' + envId + ' — your glass is dead or never summoned (click the Browser chip in THIS conversation); live glasses belong to other conversations and are untouchable (D7)' };
|
|
57
|
+
}
|
|
58
|
+
if (list.length === 1) {
|
|
59
|
+
return { session: list[0], degraded: true, note: 'no explicit identity in env — degraded single-session resolution' };
|
|
60
|
+
}
|
|
61
|
+
return { session: null, error: 'multiple live glasses and no explicit identity — pass --owner-session <id> (or GLASS_OWNER_SESSION) from the session id you already know; refusing to guess (D7)' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function connect() {
|
|
65
|
+
let reg;
|
|
66
|
+
try {
|
|
67
|
+
reg = await (await fetch(DSI + '/api/browser')).json();
|
|
68
|
+
} catch {
|
|
69
|
+
die('DSI dev server not reachable on 5175 — floor not up (dsi dsh --sync --dev --playwright-mcp)');
|
|
70
|
+
}
|
|
71
|
+
const { session, degraded, note, error } = pickSession(reg.sessions, identityFrom(process.argv));
|
|
72
|
+
if (!session) die(error ?? 'no glass session resolved — operator must click the Browser chip');
|
|
73
|
+
if (degraded || note) console.error(JSON.stringify({ warn: note ?? 'degraded', sessionId: session.id }));
|
|
74
|
+
const browser = await chromium.connectOverCDP('http://127.0.0.1:' + session.port).catch(() => null);
|
|
75
|
+
if (!browser) die('CDP connect failed on port ' + session.port);
|
|
76
|
+
return { session, browser, ctx: browser.contexts()[0] };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function pick(ctx, substr) {
|
|
80
|
+
return ctx.pages().find((p) => p.url().includes(substr));
|
|
81
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* glass-url.mjs — the dsi-glass URL path (2026-10-29).
|
|
4
|
+
*
|
|
5
|
+
* For driving NON-DSI surfaces (YouTube, docs, anything) on the operator's
|
|
6
|
+
* glass. Never touches the DSI tab: open <url> adds a NEW tab last and
|
|
7
|
+
* focuses it (tab-grammar: glass <url>); the other verbs run against the
|
|
8
|
+
* NEWEST tab, whatever it is. The DSI server is still needed — it owns the
|
|
9
|
+
* session registry (see _connect.mjs).
|
|
10
|
+
*
|
|
11
|
+
* Usage (run from the DSI repo root so playwright-core resolves):
|
|
12
|
+
* node .agents/skills/dsi-glass/scripts/glass-url.mjs open <url>
|
|
13
|
+
* node …/glass-url.mjs pages
|
|
14
|
+
* node …/glass-url.mjs eval '<js>' # async page.evaluate on newest tab
|
|
15
|
+
* node …/glass-url.mjs shot <file.png> # screenshot of newest tab
|
|
16
|
+
*/
|
|
17
|
+
import { connect, die } from './_connect.mjs';
|
|
18
|
+
|
|
19
|
+
const [cmd, ...args] = process.argv.slice(2);
|
|
20
|
+
const { session, browser, ctx } = await connect();
|
|
21
|
+
|
|
22
|
+
// newest real tab = last http(s) page; CDP page order is not insertion order
|
|
23
|
+
// and the glass placeholder (data:text/html,<title>Glass Browser…) can sit last
|
|
24
|
+
const newest = () => {
|
|
25
|
+
const pages = ctx.pages().filter((p) => p.url().startsWith('http'));
|
|
26
|
+
return pages[pages.length - 1];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (cmd === 'open') {
|
|
30
|
+
const url = args[0];
|
|
31
|
+
if (!url) die('open needs a url');
|
|
32
|
+
const page = await ctx.newPage();
|
|
33
|
+
await page.goto(url, { waitUntil: 'domcontentloaded' }).catch((e) => die('goto failed: ' + e.message));
|
|
34
|
+
await page.waitForTimeout(1500); // let the SPA settle
|
|
35
|
+
console.log(JSON.stringify({ ok: true, sessionId: session.id, url: page.url(), title: await page.title() }));
|
|
36
|
+
} else if (cmd === 'pages') {
|
|
37
|
+
console.log(JSON.stringify(await Promise.all(ctx.pages().map(async (p) => ({ url: p.url(), title: await p.title() }))), null, 1));
|
|
38
|
+
} else if (cmd === 'eval') {
|
|
39
|
+
if (!args[0]) die('eval needs a js expression');
|
|
40
|
+
console.log(JSON.stringify(await newest().evaluate(async (src) => await eval(src), args[0]), null, 1));
|
|
41
|
+
} else if (cmd === 'shot') {
|
|
42
|
+
const file = args[0] ?? 'tmp/glass-url-shot.png';
|
|
43
|
+
await newest().screenshot({ path: file });
|
|
44
|
+
console.log(JSON.stringify({ ok: true, file }));
|
|
45
|
+
} else {
|
|
46
|
+
die('unknown command: ' + cmd);
|
|
47
|
+
}
|
|
48
|
+
await browser.close();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* glass.mjs — the dsi-glass fast path (2026-10-29).
|
|
4
|
+
*
|
|
5
|
+
* ONE command replaces the discovery dance: port resolve (:5175 health →
|
|
6
|
+
* GET /api/browser → session port → /json/list → playwright-core
|
|
7
|
+
* connectOverCDP → reuse-or-create the DSI tab). The browser-session
|
|
8
|
+
* registry stays the ONLY source of truth — this script caches nothing
|
|
9
|
+
* (skill rule 3: a hardcoded port verifies a ghost).
|
|
10
|
+
*
|
|
11
|
+
* Usage (run from the DSI repo root so playwright-core resolves):
|
|
12
|
+
* node .agents/skills/dsi-glass/scripts/glass.mjs open [url]
|
|
13
|
+
* node …/glass.mjs eval '<js>' # async page.evaluate, JSON out
|
|
14
|
+
* node …/glass.mjs type '<text>' # real keystrokes into the composer
|
|
15
|
+
* node …/glass.mjs shot <file.png> # viewport screenshot
|
|
16
|
+
* node …/glass.mjs strip # suggest-strip verdict (rows,
|
|
17
|
+
* # activeIdx, ariaSel, tipCount)
|
|
18
|
+
* node …/glass.mjs pages # list glass pages (url + title)
|
|
19
|
+
*
|
|
20
|
+
* On any failure it prints the manual fallback hint (SKILL.md Step 1 —
|
|
21
|
+
* the operator clicks the Browser chip; agents never open sessions).
|
|
22
|
+
*/
|
|
23
|
+
import { connect, die } from './_connect.mjs';
|
|
24
|
+
|
|
25
|
+
const DSI = 'http://127.0.0.1:5175';
|
|
26
|
+
const [cmd, ...args] = process.argv.slice(2);
|
|
27
|
+
|
|
28
|
+
const { session, browser, ctx } = await connect();
|
|
29
|
+
|
|
30
|
+
let page = ctx.pages().find((p) => p.url().includes('5175'));
|
|
31
|
+
if (cmd === 'open') {
|
|
32
|
+
if (!page) {
|
|
33
|
+
page = await ctx.newPage();
|
|
34
|
+
await page.goto(args[0] ?? DSI, { waitUntil: 'domcontentloaded' }).catch(() => {});
|
|
35
|
+
} else if (args[0] && args[0] !== page.url()) {
|
|
36
|
+
await page.goto(args[0], { waitUntil: 'domcontentloaded' }).catch(() => {});
|
|
37
|
+
}
|
|
38
|
+
await page.waitForSelector('body', { timeout: 15000 });
|
|
39
|
+
} else if (!page) {
|
|
40
|
+
die('no DSI tab in the glass — run: glass.mjs open');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (cmd === 'open') {
|
|
44
|
+
await page.waitForTimeout(1500); // let the SPA settle
|
|
45
|
+
console.log(JSON.stringify({ ok: true, sessionId: session.id, port: session.port, url: page.url(), title: await page.title() }));
|
|
46
|
+
} else if (cmd === 'pages') {
|
|
47
|
+
console.log(JSON.stringify(await Promise.all(ctx.pages().map(async (p) => ({ url: p.url(), title: await p.title() }))), null, 1));
|
|
48
|
+
} else if (cmd === 'eval') {
|
|
49
|
+
if (!args[0]) die('eval needs a js expression');
|
|
50
|
+
console.log(JSON.stringify(await page.evaluate(async (src) => await eval(src), args[0]), null, 1));
|
|
51
|
+
} else if (cmd === 'type') {
|
|
52
|
+
if (!args[0]) die('type needs text');
|
|
53
|
+
const ta = page.locator('textarea').first();
|
|
54
|
+
await ta.click();
|
|
55
|
+
await page.keyboard.press('Meta+a');
|
|
56
|
+
await page.keyboard.press('Backspace');
|
|
57
|
+
await page.keyboard.type(args[0], { delay: 60 });
|
|
58
|
+
await page.waitForTimeout(1000); // debounce (120 ms) + fetch
|
|
59
|
+
console.log(JSON.stringify({ ok: true, typed: args[0] }));
|
|
60
|
+
} else if (cmd === 'shot') {
|
|
61
|
+
const file = args[0] ?? 'tmp/glass-shot.png';
|
|
62
|
+
await page.screenshot({ path: file });
|
|
63
|
+
console.log(JSON.stringify({ ok: true, file }));
|
|
64
|
+
} else if (cmd === 'strip') {
|
|
65
|
+
console.log(JSON.stringify(await page.evaluate(() => {
|
|
66
|
+
const strip = document.querySelector('[data-testid=suggest-strip]');
|
|
67
|
+
if (!strip) return { strip: false };
|
|
68
|
+
const chips = [...strip.querySelectorAll(':scope > .strip-chip')];
|
|
69
|
+
const tips = [...document.body.querySelectorAll(':scope > .strip-tip')];
|
|
70
|
+
return {
|
|
71
|
+
strip: true,
|
|
72
|
+
rows: chips.length,
|
|
73
|
+
labels: chips.map((c) => c.querySelector('.strip-label')?.textContent?.trim()),
|
|
74
|
+
activeIdx: chips.findIndex((c) => c.classList.contains('active')),
|
|
75
|
+
ariaSel: chips.map((c) => c.querySelector('.strip-pick')?.getAttribute('aria-selected')),
|
|
76
|
+
tipCount: tips.length,
|
|
77
|
+
tipText: tips[0]?.querySelector('pre')?.textContent?.slice(0, 80) ?? null,
|
|
78
|
+
taValue: document.querySelector('textarea')?.value
|
|
79
|
+
};
|
|
80
|
+
}), null, 1));
|
|
81
|
+
} else {
|
|
82
|
+
die('unknown command: ' + cmd);
|
|
83
|
+
}
|
|
84
|
+
await browser.close();
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: glass-url
|
|
3
|
+
description: Open and drive an ARBITRARY URL (YouTube, docs, any web page) in a NEW tab on the operator's glass browser via CDP — search, click, screenshot, read the page. NOT for verifying DSI work; that is glass-dsi. Reserved keyword /glass-url.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# glass-url — drive an arbitrary URL on the operator's glass
|
|
7
|
+
|
|
8
|
+
Companion skill: `glass-dsi` (verify DSI work on the glass — the DSI tab,
|
|
9
|
+
the floor mirror, the SSE probe). This skill is the OTHER scenario: the
|
|
10
|
+
target is some web page, and the DSI tab is none of your business.
|
|
11
|
+
|
|
12
|
+
**The dispatch rule (Tab-Juggling RCA, ADR-0045):** before any glass work
|
|
13
|
+
ask ONE question — is the surface I must drive the DSI workspace, or some
|
|
14
|
+
other URL? DSI workspace → `glass-dsi`. Any other URL → THIS skill.
|
|
15
|
+
Never navigate the DSI tab, never re-run `open` to "recover".
|
|
16
|
+
|
|
17
|
+
## Environment contract (read before the first command — RCA 2026-10-30)
|
|
18
|
+
|
|
19
|
+
- **Node lives off the default PATH here** (`~/.local/bin/node` observed
|
|
20
|
+
2026-10-30). If `node` is not found, resolve it first:
|
|
21
|
+
`bash -lc "which node"` or check `~/.local/bin`, `/opt/homebrew/bin`.
|
|
22
|
+
- **Run from the DSI repo root** (`~/agentic-ai/deepseek-insight`) so
|
|
23
|
+
`playwright-core` resolves.
|
|
24
|
+
- **Never hand unquoted JS through a shell** — a stripped quote fails
|
|
25
|
+
after the round-trip was already spent. Use `eval --b64 <base64>`.
|
|
26
|
+
- **Every ad-hoc driver snippet must `process.exit()`** — a driver that
|
|
27
|
+
played media once hung its caller for a full 120s timeout.
|
|
28
|
+
- **Script headers are contracts** — `_connect.mjs` exports
|
|
29
|
+
`die`, `connect`, `pick(ctx, substr)`; do not guess names, read the
|
|
30
|
+
header; re-read the header in the same edit that changes the code.
|
|
31
|
+
|
|
32
|
+
## The fast path — glass-url.mjs (one command per intent)
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
# from the DSI repo root (playwright-core resolves there)
|
|
36
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs yt "Dimas Senopati" --play "What's Up" --shot tmp/proof.png
|
|
37
|
+
# → search + normalized match + open watch tab + unmute/play + double
|
|
38
|
+
# gate + shot, ONE JSON with every stage, then exits. --play consumes
|
|
39
|
+
# EVERY term (artist AND title) — put both in it (ADR-0045 D9).
|
|
40
|
+
|
|
41
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs open <url> # NEW tab, focused
|
|
42
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs pages # list tabs
|
|
43
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs eval '<js>' # newest non-DSI tab
|
|
44
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs eval watch?v= '<js>' # inline: LAST arg is the js
|
|
45
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs eval --b64 <b64-js> <url-substring>
|
|
46
|
+
# quote-proof eval; anything left after the payload IS the url-substring
|
|
47
|
+
# (ADR-0045 D10 — parsed by shape, never positional fallback)
|
|
48
|
+
node .agents/skills/glass-url/scripts/glass-url.mjs shot f.png [url-substring]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
"Newest tab" = last http(s) page that is neither the glass placeholder nor
|
|
52
|
+
the DSI tab (:5175). When SEVERAL foreign tabs exist, position lies —
|
|
53
|
+
pass the URL-substring argument (the `yt` verb does this for you).
|
|
54
|
+
Every run re-resolves the session from `GET /api/browser`.
|
|
55
|
+
|
|
56
|
+
## Recipes
|
|
57
|
+
|
|
58
|
+
- **Search + play a video:** the `yt` verb IS the recipe (match
|
|
59
|
+
normalization included: apostrophes/case folded). Only drop to
|
|
60
|
+
`open` → `eval` when the flow is not search-and-play.
|
|
61
|
+
- **Read a page:** `open` → `eval` extract DOM text/links.
|
|
62
|
+
|
|
63
|
+
## Rules
|
|
64
|
+
|
|
65
|
+
1. Always `open` a NEW tab for your target — never reuse a tab you did
|
|
66
|
+
not create, never close operator tabs.
|
|
67
|
+
2. Never navigate the DSI workspace tab; it belongs to glass-dsi.
|
|
68
|
+
3. Multi-step state (click, type, drag) beyond eval/shot: drop to
|
|
69
|
+
playwright-core by hand over CDP (import `connect`/`pick` from
|
|
70
|
+
`.agents/skills/glass-url/scripts/_connect.mjs` — exports listed in
|
|
71
|
+
its header), same as glass-dsi §2–4.
|
|
72
|
+
4. If `GET /api/browser` has no session: the operator must click the
|
|
73
|
+
Browser chip first (the one gesture you cannot simulate). Say so and wait.
|
|
74
|
+
5. Verify with gates, not adjectives: a playback gate is `paused:false`
|
|
75
|
+
plus rising `currentTime`; a navigation gate is the URL/title you
|
|
76
|
+
expected; finish by asking the operator to confirm they SEE it.
|
|
77
|
+
|
|
78
|
+
## Identity (2026-10-30, RCA "the session id is gone")
|
|
79
|
+
|
|
80
|
+
The driver claims YOUR glass by identity, first-wins:
|
|
81
|
+
1. `--owner-session <id>` flag — pass the session id you already know from
|
|
82
|
+
your context when `DSH_SESSION_ID` is absent (the harness injects it
|
|
83
|
+
only when the execution carries an agent binding — dsh-shell-env
|
|
84
|
+
index.js:89 — so its absence is possible and NOT an error).
|
|
85
|
+
2. `GLASS_OWNER_SESSION` env.
|
|
86
|
+
3. `DSH_SESSION_ID` env.
|
|
87
|
+
No identity + several live glasses = loud refusal, never a guess (D7).
|
|
88
|
+
|
|
89
|
+
## The Stop-Loss Law (2026-10-30, ADR-0045 D11 — a refusal is an answer)
|
|
90
|
+
|
|
91
|
+
- **NO_MATCH is an answer.** When the yt verb exits `stage:"match"` with a
|
|
92
|
+
candidate list, REPORT that list and STOP — do not open ad-hoc search
|
|
93
|
+
tabs, do not re-search with different words, do not drop to open/eval to
|
|
94
|
+
hand-roll what the verb just refused. The operator decides the next move.
|
|
95
|
+
- **Grammar dies are answers.** A `die` ("no tab matching …", "no
|
|
96
|
+
non-DSI tab …") ends the investigation, not triggers a workaround hunt.
|
|
97
|
+
Run `pages` once to re-orient if you must, then report.
|
|
98
|
+
- **Already playing is DONE.** If the media is live on the operator's glass
|
|
99
|
+
(they say so, or a prior gate passed), the goal is met — report and stop.
|
|
100
|
+
No follow-up verification, no "one more tab". Verification serves the
|
|
101
|
+
operator's goal; when the goal is met, the correct next action is WORDS.
|
|
102
|
+
- Every extra tab and round-trip after a refusal is a token burn the
|
|
103
|
+
operator pays for. (RCA: "The Answer That Bought Another Round".)
|
|
104
|
+
|
|
105
|
+
## Failure branches (2026-10-30)
|
|
106
|
+
|
|
107
|
+
- `eval` on a cross-origin page still loading can return empty — wait and
|
|
108
|
+
re-run; do not conclude "no results".
|
|
109
|
+
- String matching needs normalization (case, apostrophes) or exact titles
|
|
110
|
+
will NO_MATCH — `yt` normalizes for you.
|
|
111
|
+
- A vite restart empties the registry and kills the Chrome — re-run from
|
|
112
|
+
the operator's chip click, same as glass-dsi.
|
|
113
|
+
- "Newest non-DSI tab" is positional and CAN pick a sibling foreign tab —
|
|
114
|
+
select by URL substring (the optional eval/shot argument), not position.
|
|
115
|
+
- Node/git missing from PATH, quoting mangling, guessed flags, and
|
|
116
|
+
non-exiting drivers each cost a full round-trip: see the environment
|
|
117
|
+
contract above; they are pre-paid now.
|