@deepseek-insight/dsi 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/build/client/_app/immutable/assets/0.DyOAkKS-.css +2 -0
  2. package/build/client/_app/immutable/assets/2.BPprd30B.css +1 -0
  3. package/build/client/_app/immutable/chunks/{CJ1fzlPC.js → BoHeiMOU.js} +1 -1
  4. package/build/client/_app/immutable/chunks/C9QAOGL1.js +2 -0
  5. package/build/client/_app/immutable/chunks/QJuSx3RG.js +1 -0
  6. package/build/client/_app/immutable/chunks/sC_8Ss7w.js +1 -0
  7. package/build/client/_app/immutable/entry/{app.DP0oSUf-.js → app.S_mxIoOg.js} +2 -2
  8. package/build/client/_app/immutable/entry/start.BPQAB3iV.js +1 -0
  9. package/build/client/_app/immutable/nodes/{1.UuScj-IS.js → 1.D20V6eTe.js} +1 -1
  10. package/build/client/_app/immutable/nodes/{2.CJRPZhbe.js → 2.BHcBdFqH.js} +223 -223
  11. package/build/client/_app/version.json +1 -1
  12. package/build/handler.js +4 -4
  13. package/build/index.js +4 -4
  14. package/build/server/chunks/chunks/{db.js-B8BUpxGM.js → db.js-DQm6CZ_r.js} +2 -2
  15. package/build/server/chunks/chunks/{db2.js-CNe3I_g2.js → db2.js-DUhFxA3a.js} +2 -2
  16. package/build/server/chunks/chunks/{dsh-connection.js-xltcocWy.js → dsh-connection.js-BgeraFA2.js} +2 -2
  17. package/build/server/chunks/chunks/{dsh-events.js-CGuF_had.js → dsh-events.js-BUkLR6ZV.js} +13 -1
  18. package/build/server/chunks/chunks/{git-probe.js-CUsTmhmV.js → git-probe.js-CLF37SRh.js} +3 -3
  19. package/build/server/chunks/chunks/{insight-config.js-CaHHJpFK.js → insight-config.js-Dp_2gB5J.js} +5 -3
  20. package/build/server/chunks/chunks/{internal2.js-Pv5iygVN.js → internal2.js-Ba5p8uFM.js} +2 -2
  21. package/build/server/chunks/chunks/registry.js-C2MdrfNv.js +954 -0
  22. package/build/server/chunks/chunks/{state.js-BCaKzPy6.js → state.js-BSiAgink.js} +2 -2
  23. package/build/server/chunks/chunks/{watcher.js-DcT3cS4v.js → watcher.js-BVKnoQUw.js} +5 -5
  24. package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-19pFOjH8.js → _server.ts.js-BjzdVwdY.js} +3 -3
  25. package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-BcUn6axe.js → _server.ts.js-3kQCQuHT.js} +6 -6
  26. package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-oMpzLZoy.js → _server.ts.js-BUBTCLFn.js} +6 -6
  27. package/build/server/chunks/entries/endpoints/api/browser/_id_/activate/_server.ts.js-CogDOHKu.js +45 -0
  28. package/build/server/chunks/entries/endpoints/api/browser/_id_/close/{_server.ts.js-DC3qPMQS.js → _server.ts.js-xOt7LnS1.js} +3 -3
  29. package/build/server/chunks/entries/endpoints/api/browser/_id_/closetab/_server.ts.js-CSmVPvSM.js +44 -0
  30. package/build/server/chunks/entries/endpoints/api/browser/_id_/element/_server.ts.js-BA40oCDW.js +43 -0
  31. package/build/server/chunks/entries/endpoints/api/browser/_id_/input/{_server.ts.js-CHNcK7BH.js → _server.ts.js-EX7eVzjp.js} +6 -5
  32. package/build/server/chunks/entries/endpoints/api/browser/_id_/mcp/{_server.ts.js-CYkOJL2O.js → _server.ts.js-B_EcmyFt.js} +3 -3
  33. package/build/server/chunks/entries/endpoints/api/browser/_id_/navigate/{_server.ts.js-DHTDi8KR.js → _server.ts.js-C3dYbyS3.js} +12 -8
  34. package/build/server/chunks/entries/endpoints/api/browser/_id_/opentab/_server.ts.js-CMhJJO25.js +45 -0
  35. package/build/server/chunks/entries/endpoints/api/browser/_id_/page/{_server.ts.js-Y5khEC2l.js → _server.ts.js-DIW4uI2S.js} +5 -4
  36. package/build/server/chunks/entries/endpoints/api/browser/_id_/stream/{_server.ts.js-BmjgfZD2.js → _server.ts.js-CIPIlq5X.js} +3 -3
  37. package/build/server/chunks/entries/endpoints/api/browser/_id_/tabs/_server.ts.js-CoEdOiml.js +34 -0
  38. package/build/server/chunks/entries/endpoints/api/browser/_id_/viewport/{_server.ts.js-IxS-goi1.js → _server.ts.js-CdELUoGT.js} +7 -5
  39. package/build/server/chunks/entries/endpoints/api/browser/{_server.ts.js-6STmJ7FD.js → _server.ts.js-ea6HpNGh.js} +15 -5
  40. package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-BVyx8gPc.js → _server.ts.js-C8XDnDwe.js} +3 -3
  41. package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-CNJbv6CC.js → _server.ts.js-BFmWshpv.js} +3 -3
  42. package/build/server/chunks/entries/endpoints/api/dsh/jobs/{_server.ts.js-CDWmXDJX.js → _server.ts.js-DATWy5mQ.js} +3 -3
  43. package/build/server/chunks/entries/endpoints/api/dsh/jobs/follow/{_server.ts.js-BDXQcQSL.js → _server.ts.js-CugOD1Zd.js} +3 -3
  44. package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-CgedtzOg.js → _server.ts.js-DCupM4da.js} +3 -3
  45. package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-ut2pK15B.js → _server.ts.js-Bh1btvr9.js} +3 -3
  46. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/{_server.ts.js-Ct2W7g4c.js → _server.ts.js-Cb8SBWVw.js} +3 -3
  47. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-Rke3V_qh.js → _server.ts.js-WI9yH2iG.js} +3 -3
  48. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-BYHm5WBA.js → _server.ts.js-bN9adUqz.js} +3 -3
  49. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-B_bEUKkt.js → _server.ts.js-Bxlm966G.js} +3 -3
  50. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-D78fGUQk.js → _server.ts.js-CM-raN_Q.js} +3 -3
  51. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-CgB_c7zG.js → _server.ts.js-BzP30TXW.js} +4 -4
  52. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-dVaW22_K.js → _server.ts.js-DvNkrTaY.js} +3 -3
  53. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-Dl8SBXdH.js → _server.ts.js-DXbCrefw.js} +3 -3
  54. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-BKDiwMIE.js → _server.ts.js-B7YRQhuJ.js} +4 -4
  55. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-D3FY2Wxt.js → _server.ts.js-C_AEaLLJ.js} +3 -3
  56. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-BWuIEdjl.js → _server.ts.js-Dj-pF5ik.js} +2 -2
  57. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-CeiQFOGP.js → _server.ts.js-BX9FWJ6g.js} +3 -3
  58. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-lMofZOns.js → _server.ts.js-CQ2Bmtu4.js} +3 -3
  59. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-CNalVHrK.js → _server.ts.js-Ha7xjnKW.js} +3 -3
  60. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-I2s8qvcV.js → _server.ts.js-BOg6llHk.js} +3 -3
  61. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-DTKELD7v.js → _server.ts.js-DBqGALzZ.js} +3 -3
  62. package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-BXSD__Gg.js → _server.ts.js-CvA8WDDb.js} +3 -3
  63. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-C9IZl2Qh.js → _server.ts.js-BfjNH40j.js} +3 -3
  64. package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-BRXu3UtL.js → _server.ts.js-De_qumyr.js} +3 -3
  65. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-DmnpiaD2.js → _server.ts.js-tFpJJHCb.js} +3 -3
  66. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-C5qCdsdm.js → _server.ts.js-DVx5GGA_.js} +3 -3
  67. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-BVUn49yf.js → _server.ts.js-DuKT-aZp.js} +3 -3
  68. package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-DuCOsoF0.js → _server.ts.js-DsWFidFY.js} +3 -3
  69. package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-2mcRY7IP.js → _server.ts.js-C6j0adQs.js} +3 -3
  70. package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-DZjaHZxU.js → _server.ts.js-BChAg4hm.js} +3 -3
  71. package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-DTVmsohJ.js → _server.ts.js-BPSHqbZN.js} +2 -2
  72. package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-DZXTArj7.js → _server.ts.js-D5CbUe5k.js} +2 -2
  73. package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-LSc5EKH7.js → _server.ts.js-B-5YihUA.js} +5 -5
  74. package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-DNjiScBe.js → _server.ts.js-DT5jRZFE.js} +5 -5
  75. package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-CHMbLoSn.js → _server.ts.js-DtW-utrd.js} +5 -5
  76. package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-BgM4JEAm.js → _server.ts.js-8AIAo9jX.js} +5 -5
  77. package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-BH0cLS8o.js → _server.ts.js-CMw96vjY.js} +5 -5
  78. package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-BzlV3cVT.js → _server.ts.js-DKufa14_.js} +5 -5
  79. package/build/server/chunks/entries/pages/{_error.svelte.js-BRyCyR0c.js → _error.svelte.js-D8-G0evO.js} +3 -3
  80. package/build/server/chunks/entries/pages/{_page.server.ts.js-DttLUwdf.js → _page.server.ts.js-7x6ZfGoB.js} +3 -3
  81. package/build/server/chunks/entries/pages/{_page.svelte.js-CKY8hz9T.js → _page.svelte.js-7xWLRsiZ.js} +333 -158
  82. package/build/server/chunks/{handler-D1oqmwyy.js → handler-BedQ0jP-.js} +3 -3
  83. package/build/server/chunks/{index.js-BZ8zP9wS.js → index.js-BF6tPPQs.js} +2 -2
  84. package/build/server/chunks/{manifest.js-CA6PuZTD.js → manifest.js-3c0p7QAl.js} +90 -55
  85. package/build/server/chunks/nodes/{0.js-DtkNHIFx.js → 0.js-B3i7sGR_.js} +3 -3
  86. package/build/server/chunks/nodes/1.js-eUf50uxI.js +9 -0
  87. package/build/server/chunks/nodes/{2.js-P1boc94A.js → 2.js-CgnRGwm6.js} +8 -8
  88. package/package.json +2 -7
  89. package/skills/dsi-kb-scenario/SKILL.md +67 -0
  90. package/template/.dsi/prompts.sqlite-shm +0 -0
  91. package/template/.dsi/prompts.sqlite-wal +0 -0
  92. package/template/.dsi/settings.yaml +8 -1
  93. package/build/client/_app/immutable/assets/0.DBVVEjy9.css +0 -2
  94. package/build/client/_app/immutable/assets/2.BhBfHqja.css +0 -1
  95. package/build/client/_app/immutable/chunks/C0tWPpc3.js +0 -2
  96. package/build/client/_app/immutable/chunks/gZFoJ-NI.js +0 -1
  97. package/build/client/_app/immutable/chunks/xkjLeSzJ.js +0 -1
  98. package/build/client/_app/immutable/entry/start.mBp3LB6J.js +0 -1
  99. package/build/server/chunks/chunks/registry.js-BTiJ1Kaj.js +0 -577
  100. package/build/server/chunks/nodes/1.js-Q6zZupRG.js +0 -9
  101. /package/build/client/_app/immutable/nodes/{0.Dm_ry-Pr.js → 0.6dTmg1Xv.js} +0 -0
@@ -9,7 +9,7 @@ import { D as DEFAULT_IMAGE_LIMITS } from '../../chunks/image-limits.js-CUdJ0f7r
9
9
  import { s as sanitizeWorkspaceProfile, d as dsiKey, t as themePrefs } from '../../chunks/theme-prefs.svelte.js-8CaWz46g.js';
10
10
  import { a as appConfig, p as previewSegments, b as parseTagWords, i as isValidTagWord, s as shouldRecordPrompt, g as getTrigger, t as triggerMode, c as searchKey, d as textPreviewOf } from '../../chunks/app-config.svelte.js-BM4WbMFB.js';
11
11
  import { c as getLocale } from '../../chunks/runtime.js-CCJi3XaY.js';
12
- import { p as page, a as afterNavigate, t, c as currentLocale, e as replaceState, u as unexpectederror1, s as sessionnotfound2, r as reapednote1, d as dshunreachable1, b as backtoworkspace2 } from '../../chunks/state.js-BCaKzPy6.js';
12
+ import { p as page, a as afterNavigate, t, c as currentLocale, e as replaceState, u as unexpectederror1, s as sessionnotfound2, r as reapednote1, d as dshunreachable1, b as backtoworkspace2 } from '../../chunks/state.js-BSiAgink.js';
13
13
  import 'yaml';
14
14
  import 'html-to-image';
15
15
  import hljs from 'highlight.js/lib/core';
@@ -21,7 +21,7 @@ import xml from 'highlight.js/lib/languages/xml';
21
21
  import css from 'highlight.js/lib/languages/css';
22
22
  import '../../chunks/uneval.js-BJ9Qq2SF.js';
23
23
  import '../../chunks/shared.js-HQ0rfhm4.js';
24
- import '../../chunks/internal2.js-Pv5iygVN.js';
24
+ import '../../chunks/internal2.js-Ba5p8uFM.js';
25
25
  import '../../chunks/utils.js-CAdNO8Tu.js';
26
26
 
27
27
  //#region src/lib/paraglide/messages/abort.js
@@ -952,6 +952,38 @@ var browserchiplabel2 = ((inputs = {}, options = {}) => {
952
952
  return en_browserchiplabel2();
953
953
  });
954
954
  //#endregion
955
+ //#region src/lib/paraglide/messages/browserclosedelete2.js
956
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
957
+ /** @typedef {{}} Browserclosedelete2Inputs */
958
+ var en_browserclosedelete2 = () => {
959
+ return `Delete the glass browser — kills the session and removes the panel`;
960
+ };
961
+ var zh_browserclosedelete2 = () => {
962
+ return `删除玻璃浏览器 — 终止会话并移除面板`;
963
+ };
964
+ var id_browserclosedelete2 = () => {
965
+ return `Hapus browser kaca — matikan sesi dan hapus panel`;
966
+ };
967
+ var es_browserclosedelete2 = () => {
968
+ return `Eliminar el navegador de vidrio — mata la sesión y quita el panel`;
969
+ };
970
+ /**
971
+ * | output |
972
+ * | --- |
973
+ * | "Delete the glass browser — kills the session and removes the panel" |
974
+ *
975
+ * @param {Browserclosedelete2Inputs} inputs
976
+ * @param {{ locale?: "en" | "zh" | "id" | "es" }} options
977
+ * @returns {LocalizedString}
978
+ */
979
+ var browserclosedelete2 = ((inputs = {}, options = {}) => {
980
+ const locale = options.locale ?? getLocale();
981
+ if (locale === "zh") return zh_browserclosedelete2();
982
+ if (locale === "id") return id_browserclosedelete2();
983
+ if (locale === "es") return es_browserclosedelete2();
984
+ return en_browserclosedelete2();
985
+ });
986
+ //#endregion
955
987
  //#region src/lib/paraglide/messages/browserforward1.js
956
988
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
957
989
  /** @typedef {{}} Browserforward1Inputs */
@@ -14840,6 +14872,7 @@ var _index_exports = /* @__PURE__ */ __exportAll({
14840
14872
  broadcastSyncedTo: () => broadcastsyncedto2,
14841
14873
  browserBack: () => browserback1,
14842
14874
  browserChipLabel: () => browserchiplabel2,
14875
+ browserCloseDelete: () => browserclosedelete2,
14843
14876
  browserForward: () => browserforward1,
14844
14877
  browserLaunching: () => browserlaunching1,
14845
14878
  browserOpenTitle: () => browseropentitle2,
@@ -15311,6 +15344,7 @@ var messages_exports = /* @__PURE__ */ __exportAll({
15311
15344
  broadcastSyncedTo: () => broadcastsyncedto2,
15312
15345
  browserBack: () => browserback1,
15313
15346
  browserChipLabel: () => browserchiplabel2,
15347
+ browserCloseDelete: () => browserclosedelete2,
15314
15348
  browserForward: () => browserforward1,
15315
15349
  browserLaunching: () => browserlaunching1,
15316
15350
  browserOpenTitle: () => browseropentitle2,
@@ -17585,8 +17619,8 @@ function ThemeToggle($$renderer, $$props) {
17585
17619
  }
17586
17620
  //#endregion
17587
17621
  //#region package.json
17588
- var version = "0.8.1";
17589
- var dsh = { "webVersion": "0.2.1-rc.2" };
17622
+ var version = "0.8.3";
17623
+ var dsh = { "webVersion": "0.2.1-rc.3" };
17590
17624
  //#endregion
17591
17625
  //#region src/lib/version.ts
17592
17626
  /**
@@ -23324,6 +23358,32 @@ function panelRowsFor(input) {
23324
23358
  function rowFactsByPanelId(rows) {
23325
23359
  return new Map(rows.map((r) => [r.panel.id, r]));
23326
23360
  }
23361
+ /**
23362
+ * The folded-glass ghost rows (ADR-0037 D4, extracted 2026-10-24 — the
23363
+ * Glass Close Ladder task 2.2): the ONE derivation the route renders.
23364
+ * Pure over the slot mirror — a removeBrowserSlot prune drops the ghost
23365
+ * (roster) and the spine nest in the same stroke (Ladder ADR D4).
23366
+ */
23367
+ function foldedGlassRowsFor(slots, depthOf, titleFor) {
23368
+ return slots.filter((s) => s.folded && s.sessionId && s.openedBy).map((s) => ({
23369
+ panel: {
23370
+ id: "glass-" + s.sessionId,
23371
+ kind: "conversation",
23372
+ sessionId: s.sessionId,
23373
+ agentPreset: null,
23374
+ width: 0
23375
+ },
23376
+ title: "Browser — " + (titleFor(s.openedBy) ?? s.openedBy),
23377
+ workspace: null,
23378
+ dead: false,
23379
+ running: true,
23380
+ depth: depthOf(s.openedBy) + 1,
23381
+ runningDescendants: 0,
23382
+ kind: "ghost",
23383
+ parentSessionId: s.openedBy,
23384
+ siblingIndex: -1
23385
+ }));
23386
+ }
23327
23387
  //#endregion
23328
23388
  //#region src/lib/components/prompt-manager/PromptsManagerDialogHeader.svelte
23329
23389
  function PromptsManagerDialogHeader($$renderer, $$props) {
@@ -26369,6 +26429,14 @@ function groupTurns(entries) {
26369
26429
  continue;
26370
26430
  }
26371
26431
  if (entry.kind === "turn-lifecycle") continue;
26432
+ if (entry.kind === "developer-message") {
26433
+ groups.push({
26434
+ kind: "developer",
26435
+ key: entry.id,
26436
+ entry
26437
+ });
26438
+ continue;
26439
+ }
26372
26440
  const side = entry;
26373
26441
  const last = groups[groups.length - 1];
26374
26442
  if (last?.kind === "assistant-turn") last.entries.push(side);
@@ -30398,8 +30466,11 @@ function ConversationScrollArea($$renderer, $$props) {
30398
30466
  open: openChipId === group.entry.id});
30399
30467
  $$renderer.push(`<!----></div>`);
30400
30468
  }});
30401
- } else if (group.kind === "prompt" || group.kind === "context") {
30469
+ } else if (group.kind === "developer") {
30402
30470
  $$renderer.push("<!--[1-->");
30471
+ $$renderer.push(`<div data-testid="developer-message-row"${attr("data-seq", group.entry.seq)} class="mx-auto max-w-2xl rounded-md border border-text-muted/20 bg-surface0/5 px-3 py-1.5 text-xs text-text-muted"><span class="mr-1.5 font-medium uppercase tracking-wide opacity-70">developer</span> <span data-testid="developer-message-text">${escape_html(group.entry.text)}</span></div>`);
30472
+ } else if (group.kind === "prompt" || group.kind === "context") {
30473
+ $$renderer.push("<!--[2-->");
30403
30474
  PromptBubble($$renderer, {
30404
30475
  text: group.kind === "prompt" ? group.entry.text : void 0,
30405
30476
  time: group.kind === "prompt" ? group.entry.time : group.entries[0]?.time,
@@ -32836,6 +32907,19 @@ function persistBrowserSlots(slots, storage = defaultStorage()) {
32836
32907
  * plain .ts module stays rune-free). Rows and the route derive the
32837
32908
  * folded set from it; persistBrowserSlots is the only writer. */
32838
32909
  var browserSlotStore = writable([]);
32910
+ /**
32911
+ * Drop the slot entry whose glass session died (The Glass Close Ladder
32912
+ * ADR 2026-10-24 D4): the live prune — a dead glass's entry is REMOVED,
32913
+ * never folded, so the roster ghost and the spine nest drop with it in
32914
+ * the same mirror stroke, no reload. Unknown id = quiet no-op (false).
32915
+ */
32916
+ function removeBrowserSlot(sessionId, storage = defaultStorage()) {
32917
+ const slots = readBrowserSlots(storage);
32918
+ const next = slots.filter((s) => s.sessionId !== sessionId);
32919
+ if (next.length === slots.length) return false;
32920
+ persistBrowserSlots(next, storage);
32921
+ return true;
32922
+ }
32839
32923
  /** Seed the mirror from storage (boot). */
32840
32924
  function primeBrowserSlotStore(storage = defaultStorage()) {
32841
32925
  browserSlotStore.set(readBrowserSlots(storage));
@@ -32845,7 +32929,7 @@ function primeBrowserSlotStore(storage = defaultStorage()) {
32845
32929
  function BrowserClosed($$renderer, $$props) {
32846
32930
  $$renderer.component(($$renderer) => {
32847
32931
  let { closing = false} = $$props;
32848
- $$renderer.push(`<button data-svelte="BrowserClosed" class="rounded px-1 hover:bg-surface-active" data-testid="browse-close"${attr("disabled", closing, true)}>✕</button>`);
32932
+ $$renderer.push(`<button data-svelte="BrowserClosed" class="rounded hover:bg-surface-active svelte-18cg1lj" data-testid="browse-close"${attr("aria-label", browserclosedelete2())}${attr("title", browserclosedelete2())}${attr("disabled", closing, true)}><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="red" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"></path><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path><path d="M10 11v6"></path><path d="M14 11v6"></path></svg></button>`);
32849
32933
  });
32850
32934
  }
32851
32935
  //#endregion
@@ -32876,13 +32960,28 @@ function BrowserHeader($$renderer, $$props) {
32876
32960
  //#region src/lib/components/browser/BrowserContent.svelte
32877
32961
  function BrowserContent($$renderer, $$props) {
32878
32962
  $$renderer.component(($$renderer) => {
32879
- let { status, failReason = null, wrap = null, canvas = null, ondown, onup, onmove, onwheel, onkey } = $$props;
32963
+ let { status, failReason = null, wrap = null, canvas = null, ondown, onup, onmove, onwheel, onkey, oncontextmenu, ctxMenu = null, onctxaction, onctxclose } = $$props;
32880
32964
  if (status === "failed") {
32881
32965
  $$renderer.push("<!--[0-->");
32882
32966
  $$renderer.push(`<div data-svelte="BrowserContent" class="p-3 text-xs text-red-500" data-testid="browser-failed">${escape_html(browsersessionfailed2({ reason: failReason ?? "" }))}</div>`);
32883
32967
  } else {
32884
32968
  $$renderer.push("<!--[-1-->");
32885
32969
  $$renderer.push(`<div data-svelte="BrowserContent" class="relative min-h-0 flex-1 overflow-hidden" data-testid="browser-glass"><canvas class="h-full w-full object-contain" tabindex="0"></canvas> `);
32970
+ if (ctxMenu) {
32971
+ $$renderer.push("<!--[0-->");
32972
+ $$renderer.push(`<div class="absolute z-50 min-w-44 rounded-md border border-zinc-700 bg-white py-1 text-xs shadow-xl"${attr_style(`left:${stringify(ctxMenu.px)}px; top:${stringify(ctxMenu.py)}px`)} data-testid="browser-ctxmenu">`);
32973
+ if (ctxMenu.src) {
32974
+ $$renderer.push("<!--[0-->");
32975
+ $$renderer.push(`<button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Open image in new tab</button> <button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Copy image address</button>`);
32976
+ } else $$renderer.push("<!--[-1-->");
32977
+ $$renderer.push(`<!--]--> `);
32978
+ if (ctxMenu.href) {
32979
+ $$renderer.push("<!--[0-->");
32980
+ $$renderer.push(`<button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Open link in new tab</button> <button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Copy link address</button>`);
32981
+ } else $$renderer.push("<!--[-1-->");
32982
+ $$renderer.push(`<!--]--> <button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Reload</button> <div class="my-1 border-t border-zinc-300"></div> <button type="button" class="block w-full px-3 py-1.5 text-left text-zinc-900 hover:bg-zinc-200" data-testid="browser-ctxmenu-item">Send right-click to the page</button></div>`);
32983
+ } else $$renderer.push("<!--[-1-->");
32984
+ $$renderer.push(`<!--]--> `);
32886
32985
  if (status !== "live") {
32887
32986
  $$renderer.push("<!--[0-->");
32888
32987
  $$renderer.push(`<div class="absolute inset-0 grid place-items-center text-xs opacity-60" data-testid="browser-placeholder">${escape_html(status === "opening" ? browserlaunching1() : browserwaitingframes2())}</div>`);
@@ -32940,6 +33039,99 @@ function createMoveCoalescer(raf, fire) {
32940
33039
  } };
32941
33040
  }
32942
33041
  //#endregion
33042
+ //#region src/lib/utils/sidebar-prefs.ts
33043
+ /**
33044
+ * Sidebar preferences — persisted layout state for the conversation page's
33045
+ * app sidebar (OCI ControlRail pattern, 2026-08-23).
33046
+ *
33047
+ * Pure localStorage round-trip: SSR-safe (no localStorage → defaults), junk
33048
+ * payloads and out-of-range widths fall back instead of throwing into the
33049
+ * page, and writes are best-effort (a full or blocked store must not break
33050
+ * the layout — it only loses persistence for that session).
33051
+ *
33052
+ * Workspace profiles (storage-profile.ts): `?profile=widi` moves the key
33053
+ * to `dsi-sidebar_widi` — each desk remembers its own rail layout.
33054
+ */
33055
+ /** localStorage base key — one JSON blob `{ collapsed, width }`. */
33056
+ var STORAGE_KEY = "dsi-sidebar";
33057
+ /** The single width bound site — load-side sanitizing and drag clamping
33058
+ * both route through here so the bounds exist in exactly one place.
33059
+ * Bounds read the config store (layout load has landed them before any
33060
+ * mount; constants are the fallback defaults). */
33061
+ function clampSidebarWidth(width) {
33062
+ const { minWidth, maxWidth } = appConfig().sidebar;
33063
+ return Math.max(minWidth, Math.min(maxWidth, width));
33064
+ }
33065
+ /** Config-driven default width (clamped through the single bound site). */
33066
+ function defaultSidebarWidth() {
33067
+ return clampSidebarWidth(appConfig().sidebar.defaultWidth);
33068
+ }
33069
+ /** Persisted defaults when nothing (usable) is stored. */
33070
+ function defaultSidebarPrefs() {
33071
+ return {
33072
+ collapsed: false,
33073
+ width: defaultSidebarWidth()
33074
+ };
33075
+ }
33076
+ /**
33077
+ * Read prefs from localStorage.
33078
+ * @param profile sanitized workspace profile (null/omitted = default desk).
33079
+ * @returns stored prefs when parseable, defaults when absent, junk, or SSR.
33080
+ */
33081
+ function loadSidebarPrefs(profile) {
33082
+ if (typeof localStorage === "undefined") return defaultSidebarPrefs();
33083
+ try {
33084
+ const raw = localStorage.getItem(dsiKey(STORAGE_KEY, profile));
33085
+ if (!raw) return defaultSidebarPrefs();
33086
+ const parsed = JSON.parse(raw);
33087
+ if (parsed === null || typeof parsed !== "object") return defaultSidebarPrefs();
33088
+ return {
33089
+ collapsed: parsed.collapsed === true,
33090
+ width: clampSidebarWidth(typeof parsed.width === "number" && Number.isFinite(parsed.width) ? parsed.width : defaultSidebarWidth())
33091
+ };
33092
+ } catch {
33093
+ return defaultSidebarPrefs();
33094
+ }
33095
+ }
33096
+ //#endregion
33097
+ //#region src/lib/services/workspace/page-prefs.svelte.ts
33098
+ var PagePrefs = class {
33099
+ /** Width preset for the next conversation panel (clamped). */
33100
+ panelWidth = 0;
33101
+ /** Floor zoom (clamped). */
33102
+ zoom = 1;
33103
+ /** Explorer Layout split (clamped). */
33104
+ treePct = 0;
33105
+ /** Sidebar rail state (OCI ControlRail port). */
33106
+ sidebarCollapsed = false;
33107
+ sidebarWidth = 0;
33108
+ #profile = null;
33109
+ #initialised = false;
33110
+ /** Seed every field from the persisted blobs — exactly once, BEFORE
33111
+ * the route reads any field (makePanel reads panelWidth during the
33112
+ * seed reset). Intentional initial capture per the route's old
33113
+ * "svelte-ignore state_referenced_locally" comments: prefs seed the
33114
+ * layout once; later writes go through the save effects. */
33115
+ init(profile) {
33116
+ this.#initialised = true;
33117
+ this.#profile = profile;
33118
+ const floor = loadPanelPrefs(profile);
33119
+ this.panelWidth = clampPanelWidth(floor.panelWidth);
33120
+ this.zoom = clampPanelZoom(floor.zoom);
33121
+ this.treePct = clampTreePct(floor.treePct);
33122
+ const sidebar = loadSidebarPrefs(profile);
33123
+ this.sidebarCollapsed = sidebar.collapsed;
33124
+ this.sidebarWidth = sidebar.width;
33125
+ }
33126
+ get profile() {
33127
+ return this.#profile;
33128
+ }
33129
+ /** Sidebar persistence (the route's old sidebar save effect). Call
33130
+ * during component initialisation — `$effect` needs an owner. */
33131
+ installSidebarAutoSave() {}
33132
+ };
33133
+ var pagePrefs = new PagePrefs();
33134
+ //#endregion
32943
33135
  //#region src/lib/components/browser/BrowserPanel.svelte
32944
33136
  function BrowserPanel($$renderer, $$props) {
32945
33137
  $$renderer.component(($$renderer) => {
@@ -32959,6 +33151,9 @@ function BrowserPanel($$renderer, $$props) {
32959
33151
  let proxyBusy = false;
32960
33152
  let canBack = false;
32961
33153
  let canForward = false;
33154
+ /** ADR-0040 D5: the tab strip — the registry's tab list mirrored here;
33155
+ * the SERVER stays the authority (activeTargetId), this is a view. */
33156
+ let tabs = [];
32962
33157
  const moves = createMoveCoalescer((cb) => requestAnimationFrame(cb), (x, y) => void post());
32963
33158
  async function post(input) {}
32964
33159
  function toViewportCx(e) {
@@ -32984,6 +33179,67 @@ function BrowserPanel($$renderer, $$props) {
32984
33179
  const p = toViewportCx(e);
32985
33180
  if (p) moves.push(p.x, p.y);
32986
33181
  }
33182
+ /** BUGFIX 2026-10-25 (operator: YouTube's right-click menu never appears
33183
+ * in the glass): only the LEFT button was dispatched. Right-click must
33184
+ * reach the page as a right mousePressed/Released pair — Chrome paints
33185
+ * its OWN context menu and the screencast mirrors it. preventDefault
33186
+ * stops the DSI page's menu from covering the glass. */
33187
+ /** BUGFIX 2026-10-25 round 2: Chrome's NATIVE right-click menu is browser
33188
+ * UI — it never renders in the page, so the screencast can never show it
33189
+ * (only in-page custom menus like YouTube's player menu can mirror). So
33190
+ * DSI supplies its OWN menu: what the right-click landed on comes from
33191
+ * the /element data pass; actions ride the wire (opentab/clipboard/the
33192
+ * page's own menu via a dispatched right-click). */
33193
+ let ctxMenu = null;
33194
+ let ctxPoint = null;
33195
+ function onContextMenu(e) {
33196
+ e.preventDefault();
33197
+ ctxPoint = toViewportCx(e);
33198
+ const rect = wrap?.getBoundingClientRect();
33199
+ const menuW = 190, menuH = 210;
33200
+ const px = rect ? Math.min(e.clientX - rect.left, rect.width - menuW - 6) : e.clientX;
33201
+ const py = rect ? Math.min(e.clientY - rect.top, rect.height - menuH - 6) : e.clientY;
33202
+ ctxMenu = {
33203
+ px: Math.max(6, px),
33204
+ py: Math.max(6, py),
33205
+ href: null,
33206
+ src: null
33207
+ };
33208
+ }
33209
+ function ctxClose() {
33210
+ ctxMenu = null;
33211
+ }
33212
+ function ctxSendRightClick() {
33213
+ const p = ctxPoint;
33214
+ ctxClose();
33215
+ if (!p) return;
33216
+ post({
33217
+ x: p.x,
33218
+ y: p.y});
33219
+ post({
33220
+ x: p.x,
33221
+ y: p.y});
33222
+ }
33223
+ async function ctxOpenTab(url) {
33224
+ ctxClose();
33225
+ }
33226
+ function ctxAct(action) {
33227
+ const m = ctxMenu;
33228
+ ctxClose();
33229
+ if (!m) return;
33230
+ if (action === "open-image" && m.src) return void ctxOpenTab(m.src);
33231
+ if (action === "copy-image" && m.src) return void ctxCopy(m.src);
33232
+ if (action === "open-link" && m.href) return void ctxOpenTab(m.href);
33233
+ if (action === "copy-link" && m.href) return void ctxCopy(m.href);
33234
+ if (action === "reload") return void proxy();
33235
+ if (action === "send-right-click") return ctxSendRightClick();
33236
+ }
33237
+ async function ctxCopy(text) {
33238
+ ctxClose();
33239
+ try {
33240
+ await navigator.clipboard.writeText(text);
33241
+ } catch {}
33242
+ }
32987
33243
  function onWheel(e) {
32988
33244
  const p = toViewportCx(e);
32989
33245
  if (!p) return;
@@ -33028,7 +33284,7 @@ function BrowserPanel($$renderer, $$props) {
33028
33284
  let $$settled = true;
33029
33285
  let $$inner_renderer;
33030
33286
  function $$render_inner($$renderer) {
33031
- $$renderer.push(`<div data-svelte="BrowserPanel" class="flex h-full flex-col bg-surface-elevated" data-testid="panel-browser">`);
33287
+ $$renderer.push(`<div data-svelte="BrowserPanel" class="relative flex h-full flex-col bg-surface-elevated" data-testid="panel-browser">`);
33032
33288
  BrowserHeader($$renderer, {
33033
33289
  status,
33034
33290
  reBound,
@@ -33053,6 +33309,23 @@ function BrowserPanel($$renderer, $$props) {
33053
33309
  $$renderer.push(`<!--]--></div>`);
33054
33310
  } else $$renderer.push("<!--[-1-->");
33055
33311
  $$renderer.push(`<!--]--> `);
33312
+ if (tabs.length >= 1) {
33313
+ $$renderer.push("<!--[0-->");
33314
+ $$renderer.push(`<div class="flex items-center gap-1 overflow-x-auto border-b border-surface-border px-1 py-0.5 text-[11px]" data-testid="browser-tab-strip"><!--[-->`);
33315
+ const each_array = ensure_array_like(tabs);
33316
+ for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
33317
+ let t = each_array[$$index];
33318
+ $$renderer.push(`<div class="relative shrink-0"><button type="button"${attr_class("w-32 truncate rounded px-1.5 py-0.5 pr-4 text-left whitespace-nowrap", void 0, {
33319
+ "bg-blue-200": t.active,
33320
+ "text-blue-900": t.active,
33321
+ "text-zinc-600": !t.active,
33322
+ "hover:bg-zinc-100": !t.active,
33323
+ "font-medium": t.active
33324
+ })}${attr("disabled", t.active, true)} data-testid="browser-tab"${attr("data-active", t.active)}${attr("title", t.url)}>${escape_html(t.title || t.url || "tab")}</button> <button type="button" class="absolute right-0.5 top-1/2 -translate-y-1/2 rounded text-[10px] leading-none text-red-500 hover:bg-red-500 hover:text-white" title="Close tab" data-testid="browser-tab-close">✕</button></div>`);
33325
+ }
33326
+ $$renderer.push(`<!--]--></div>`);
33327
+ } else $$renderer.push("<!--[-1-->");
33328
+ $$renderer.push(`<!--]--> `);
33056
33329
  BrowserContent($$renderer, {
33057
33330
  status,
33058
33331
  failReason,
@@ -33061,6 +33334,10 @@ function BrowserPanel($$renderer, $$props) {
33061
33334
  onmove: onPointerMove,
33062
33335
  onwheel: onWheel,
33063
33336
  onkey: onKey,
33337
+ ctxMenu,
33338
+ onctxaction: ctxAct,
33339
+ onctxclose: ctxClose,
33340
+ oncontextmenu: onContextMenu,
33064
33341
  get wrap() {
33065
33342
  return wrap;
33066
33343
  },
@@ -34729,99 +35006,6 @@ function EmptyFloor($$renderer, $$props) {
34729
35006
  });
34730
35007
  }
34731
35008
  //#endregion
34732
- //#region src/lib/utils/sidebar-prefs.ts
34733
- /**
34734
- * Sidebar preferences — persisted layout state for the conversation page's
34735
- * app sidebar (OCI ControlRail pattern, 2026-08-23).
34736
- *
34737
- * Pure localStorage round-trip: SSR-safe (no localStorage → defaults), junk
34738
- * payloads and out-of-range widths fall back instead of throwing into the
34739
- * page, and writes are best-effort (a full or blocked store must not break
34740
- * the layout — it only loses persistence for that session).
34741
- *
34742
- * Workspace profiles (storage-profile.ts): `?profile=widi` moves the key
34743
- * to `dsi-sidebar_widi` — each desk remembers its own rail layout.
34744
- */
34745
- /** localStorage base key — one JSON blob `{ collapsed, width }`. */
34746
- var STORAGE_KEY = "dsi-sidebar";
34747
- /** The single width bound site — load-side sanitizing and drag clamping
34748
- * both route through here so the bounds exist in exactly one place.
34749
- * Bounds read the config store (layout load has landed them before any
34750
- * mount; constants are the fallback defaults). */
34751
- function clampSidebarWidth(width) {
34752
- const { minWidth, maxWidth } = appConfig().sidebar;
34753
- return Math.max(minWidth, Math.min(maxWidth, width));
34754
- }
34755
- /** Config-driven default width (clamped through the single bound site). */
34756
- function defaultSidebarWidth() {
34757
- return clampSidebarWidth(appConfig().sidebar.defaultWidth);
34758
- }
34759
- /** Persisted defaults when nothing (usable) is stored. */
34760
- function defaultSidebarPrefs() {
34761
- return {
34762
- collapsed: false,
34763
- width: defaultSidebarWidth()
34764
- };
34765
- }
34766
- /**
34767
- * Read prefs from localStorage.
34768
- * @param profile sanitized workspace profile (null/omitted = default desk).
34769
- * @returns stored prefs when parseable, defaults when absent, junk, or SSR.
34770
- */
34771
- function loadSidebarPrefs(profile) {
34772
- if (typeof localStorage === "undefined") return defaultSidebarPrefs();
34773
- try {
34774
- const raw = localStorage.getItem(dsiKey(STORAGE_KEY, profile));
34775
- if (!raw) return defaultSidebarPrefs();
34776
- const parsed = JSON.parse(raw);
34777
- if (parsed === null || typeof parsed !== "object") return defaultSidebarPrefs();
34778
- return {
34779
- collapsed: parsed.collapsed === true,
34780
- width: clampSidebarWidth(typeof parsed.width === "number" && Number.isFinite(parsed.width) ? parsed.width : defaultSidebarWidth())
34781
- };
34782
- } catch {
34783
- return defaultSidebarPrefs();
34784
- }
34785
- }
34786
- //#endregion
34787
- //#region src/lib/services/workspace/page-prefs.svelte.ts
34788
- var PagePrefs = class {
34789
- /** Width preset for the next conversation panel (clamped). */
34790
- panelWidth = 0;
34791
- /** Floor zoom (clamped). */
34792
- zoom = 1;
34793
- /** Explorer Layout split (clamped). */
34794
- treePct = 0;
34795
- /** Sidebar rail state (OCI ControlRail port). */
34796
- sidebarCollapsed = false;
34797
- sidebarWidth = 0;
34798
- #profile = null;
34799
- #initialised = false;
34800
- /** Seed every field from the persisted blobs — exactly once, BEFORE
34801
- * the route reads any field (makePanel reads panelWidth during the
34802
- * seed reset). Intentional initial capture per the route's old
34803
- * "svelte-ignore state_referenced_locally" comments: prefs seed the
34804
- * layout once; later writes go through the save effects. */
34805
- init(profile) {
34806
- this.#initialised = true;
34807
- this.#profile = profile;
34808
- const floor = loadPanelPrefs(profile);
34809
- this.panelWidth = clampPanelWidth(floor.panelWidth);
34810
- this.zoom = clampPanelZoom(floor.zoom);
34811
- this.treePct = clampTreePct(floor.treePct);
34812
- const sidebar = loadSidebarPrefs(profile);
34813
- this.sidebarCollapsed = sidebar.collapsed;
34814
- this.sidebarWidth = sidebar.width;
34815
- }
34816
- get profile() {
34817
- return this.#profile;
34818
- }
34819
- /** Sidebar persistence (the route's old sidebar save effect). Call
34820
- * during component initialisation — `$effect` needs an owner. */
34821
- installSidebarAutoSave() {}
34822
- };
34823
- var pagePrefs = new PagePrefs();
34824
- //#endregion
34825
35009
  //#region src/lib/services/conversation/spine-feed.svelte.ts
34826
35010
  var spineFeed = {
34827
35011
  rows: [],
@@ -34911,6 +35095,18 @@ function workspaceExplorerSlot(panels, selectedIndex, facts) {
34911
35095
  return focusedInsertionSlot(panels, selectedIndex, facts.parentOf);
34912
35096
  }
34913
35097
  /**
35098
+ * The glass FIRST slot under its opener (operator ruling 2026-10-24):
35099
+ * a summoned/spawned glass lands DIRECTLY below its opener - past the
35100
+ * opener document tail, BEFORE every adopted child - matching the
35101
+ * spine tree wire order -1 (the ghost always reads as the first
35102
+ * child). Supersedes the region-end placement for the glass only.
35103
+ */
35104
+ function glassFirstSlot(panels, openedBy) {
35105
+ const openerIdx = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === openedBy);
35106
+ if (openerIdx === -1) return panels.length;
35107
+ return docTailEnd(panels, openerIdx, openedBy) + 1;
35108
+ }
35109
+ /**
34914
35110
  * Insert-before-focused slot (operator spec, 2026-08-31): a fresh panel
34915
35111
  * lands at the FOCUSED panel's index — immediately before it — and takes
34916
35112
  * focus (the route selects it). 0 when nothing is focused (empty floor,
@@ -34938,7 +35134,7 @@ function focusedInsertionSlot(panels, selectedIndex, parentOf) {
34938
35134
  const focus = Math.max(0, selectedIndex);
34939
35135
  const focused = panels[focus];
34940
35136
  if (focused === void 0) return 0;
34941
- if (focused.kind === "workspace-explorer" || focused.kind === "workspace-file") {
35137
+ if (focused.kind === "workspace-explorer" || focused.kind === "workspace-file" || focused.kind === "browser") {
34942
35138
  const block = blockBounds(panels, focus, { parentOf });
34943
35139
  return block === null ? focus : block[0];
34944
35140
  }
@@ -34953,34 +35149,6 @@ function focusedInsertionSlot(panels, selectedIndex, parentOf) {
34953
35149
  const headIndex = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === head);
34954
35150
  return headIndex >= 0 ? Math.min(headIndex, focus) : focus;
34955
35151
  }
34956
- /**
34957
- * Lineage-order slot below an OPEN spawner (operator spec, 2026-08-28):
34958
- * after the spawner AND after every already-adopted sibling that PRECEDES
34959
- * the child in wire order — plus those siblings' own panels (an adopted
34960
- * subtree travels whole). Ghost clicks and sub-agent replacements keep the
34961
- * family region in the ghost list's order regardless of arrival sequence:
34962
- * adopting sub2 then sub3 lands |A|sub2|sub3|, a late sub1 slots back to
34963
- * |A|sub1|sub2|sub3|.
34964
- */
34965
- function lineagePinnedSlot(panels, childrenByParent, parentSessionId, childSessionId) {
34966
- let base = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === parentSessionId) + 1;
34967
- base = docTailEnd(panels, base - 1, parentSessionId) + 1;
34968
- const siblings = childrenByParent.get(parentSessionId) ?? [];
34969
- const mine = siblings.findIndex((s) => s.sessionId === childSessionId);
34970
- const closure = new Set(siblings.slice(0, mine < 0 ? siblings.length : mine).map((s) => s.sessionId));
34971
- const stack = [...closure];
34972
- while (stack.length > 0) for (const k of childrenByParent.get(stack.pop()) ?? []) if (!closure.has(k.sessionId)) {
34973
- closure.add(k.sessionId);
34974
- stack.push(k.sessionId);
34975
- }
34976
- let index = base;
34977
- while (index < panels.length) {
34978
- const at = panels[index];
34979
- if (at.kind !== "conversation" || !closure.has(at.sessionId)) break;
34980
- index++;
34981
- }
34982
- return index;
34983
- }
34984
35152
  //#endregion
34985
35153
  //#region src/lib/services/panels/floor-registry/chrome.ts
34986
35154
  /**
@@ -35021,7 +35189,7 @@ function openBrowserPanel(index, keepSelection = false, width, openedBy, session
35021
35189
  return;
35022
35190
  }
35023
35191
  const prevSelected = st.selectedPanelId;
35024
- const slot = index !== void 0 ? Math.max(0, Math.min(index, st.panels.length)) : openedBy !== void 0 && d$4().getLineageFacts().childrenByParent ? pinnedSlotIndex(openedBy, "browser") : insertionSlot();
35192
+ const slot = index !== void 0 ? Math.max(0, Math.min(index, st.panels.length)) : openedBy !== void 0 ? glassFirstSlot(st.panels, openedBy) : insertionSlot();
35025
35193
  insertPanel(makeBrowserPanel(width, openedBy, sessionId), slot);
35026
35194
  if (keepSelection && prevSelected !== null && prevSelected !== void 0) {
35027
35195
  if (st.panels.some((p) => p.id === prevSelected)) st.selectedPanelId = prevSelected;
@@ -35325,9 +35493,6 @@ function insertPanel(entry, index) {
35325
35493
  function insertionSlot() {
35326
35494
  return focusedInsertionSlot(st.panels, selectedIndex_(), d$1().getMoveFacts().parentOf);
35327
35495
  }
35328
- function pinnedSlotIndex(parentSessionId, childSessionId) {
35329
- return lineagePinnedSlot(st.panels, d$1().getLineageFacts().childrenByParent, parentSessionId, childSessionId);
35330
- }
35331
35496
  //#endregion
35332
35497
  //#region src/lib/services/panels/floor-registry.svelte.ts
35333
35498
  var deps = null;
@@ -35430,6 +35595,27 @@ function selectedIndex_() {
35430
35595
  return st.panels.findIndex((p) => p.id === st.selectedPanelId);
35431
35596
  }
35432
35597
  //#endregion
35598
+ //#region src/lib/services/panels/glass-release.ts
35599
+ /**
35600
+ * glass-release — the Glass Close Ladder executor (ADR 2026-10-24 D1/D3):
35601
+ * the ONE wire-kill caller. The registry stays the only teardown authority;
35602
+ * this module merely knocks on its door — fire-and-forget POST close per
35603
+ * doomed glass id, an ok answer pruning the slot entry LIVE (D4). A network
35604
+ * failure swallows (the reaper stays the net); a refusal never prunes.
35605
+ * Pure seam shape: fetch and the prune are injectable for tests.
35606
+ */
35607
+ async function releaseGlasses(sessionIds, deps = {}) {
35608
+ if (sessionIds.length === 0) return;
35609
+ const doFetch = deps.fetchImpl ?? fetch;
35610
+ const apiBase = deps.apiBase ?? "";
35611
+ await Promise.all(sessionIds.map(async (id) => {
35612
+ try {
35613
+ if (!(await doFetch(apiBase + "/api/browser/" + id + "/close", { method: "POST" })).ok) return;
35614
+ (deps.prune ?? ((sid) => removeBrowserSlot(sid)))(id);
35615
+ } catch {}
35616
+ }));
35617
+ }
35618
+ //#endregion
35433
35619
  //#region src/lib/services/panels/floor-reads.svelte.ts
35434
35620
  var floorReads = {
35435
35621
  get spineRows() {
@@ -35573,7 +35759,8 @@ function _page($$renderer, $$props) {
35573
35759
  setPanelWidthPreset: (w) => pagePrefs.panelWidth = w,
35574
35760
  setTreePct: (pct) => pagePrefs.treePct = pct,
35575
35761
  getSettingsHomes: () => appConfig().settingsHomes,
35576
- applyTerminalDeskAction: (a) => void 0
35762
+ applyTerminalDeskAction: (a) => void 0,
35763
+ releaseGlasses: (ids) => void releaseGlasses(ids)
35577
35764
  });
35578
35765
  initFloor(seededInitially ? [makePanel(seedSessionId(), seedCold?.agentPreset ?? null)] : initialPrefs.panels, seededInitially ? null : initialPrefs.selectedPanelId);
35579
35766
  if (seededInitially) selectPanel(floor.panels[0]?.id ?? null);
@@ -35596,9 +35783,11 @@ function _page($$renderer, $$props) {
35596
35783
  } catch {}
35597
35784
  }
35598
35785
  /** Bring ONE folded glass back (the summon; the replace verb is the
35599
- * same re-bind — the glass owns its slot identity, D4). */
35600
- function summonGlass(glass) {
35601
- openBrowserPanel(glass.index, true, glass.width, glass.openedBy, glass.sessionId);
35786
+ * same re-bind — the glass owns its slot identity, D4). keepSelection:
35787
+ * boot restore keeps the operator's focus (true, the default); the
35788
+ * ghost-click summon focuses the GLASS (false, Ladder focus contract). */
35789
+ function summonGlass(glass, opts = {}) {
35790
+ openBrowserPanel(glass.index, opts.keepSelection ?? true, glass.width, glass.openedBy, glass.sessionId);
35602
35791
  }
35603
35792
  primeBrowserSlotStore();
35604
35793
  restoreBrowserGlasses();
@@ -35614,24 +35803,7 @@ function _page($$renderer, $$props) {
35614
35803
  const spineWorkspaces = derived(() => floorReads.spineWorkspaces);
35615
35804
  const lineageFacts = derived(() => floorReads.lineageFacts);
35616
35805
  const conversationPanels = derived(() => floorReads.conversationPanels);
35617
- const allFoldedGlassRows = derived(() => store_get($$store_subs ??= {}, "$browserSlotStore", browserSlotStore).filter((s) => s.folded && s.sessionId && s.openedBy).map((s) => ({
35618
- panel: {
35619
- id: "glass-" + s.sessionId,
35620
- kind: "conversation",
35621
- sessionId: s.sessionId,
35622
- agentPreset: null,
35623
- width: 0
35624
- },
35625
- title: "Browser — " + (panelTitleFor(s.openedBy) ?? s.openedBy),
35626
- workspace: null,
35627
- dead: false,
35628
- running: true,
35629
- depth: (lineageFacts().depthById.get(s.openedBy) ?? 0) + 1,
35630
- runningDescendants: 0,
35631
- kind: "ghost",
35632
- parentSessionId: s.openedBy,
35633
- siblingIndex: -1
35634
- })));
35806
+ const allFoldedGlassRows = derived(() => foldedGlassRowsFor(store_get($$store_subs ??= {}, "$browserSlotStore", browserSlotStore), (id) => lineageFacts().depthById.get(id) ?? 0, (id) => panelTitleFor(id)));
35635
35807
  const spineGlassRows = derived(() => allFoldedGlassRows().filter((g) => !conversationPanels().some((p) => p.sessionId === g.parentSessionId)).map((g) => ({
35636
35808
  sessionId: g.panel.sessionId,
35637
35809
  openedBy: g.parentSessionId,
@@ -35704,7 +35876,10 @@ function _page($$renderer, $$props) {
35704
35876
  $$renderer.push("<!--[1-->");
35705
35877
  BrowserManager($$renderer, {
35706
35878
  browserSessionId: "browserSessionId" in panel ? panel.browserSessionId : void 0,
35707
- onclose: () => removePanel(panel.id),
35879
+ onclose: () => {
35880
+ if ("browserSessionId" in panel && panel.browserSessionId) releaseGlasses([panel.browserSessionId]);
35881
+ removePanel(panel.id);
35882
+ },
35708
35883
  openedBy: panel.openedBy,
35709
35884
  parentTitle: panel.openedBy ? panelTitleFor(panel.openedBy) ?? panel.openedBy : null,
35710
35885
  depth: panel.openedBy ? lineageFacts().depthById.get(panel.openedBy) ?? 0 : 0});
@@ -35942,4 +36117,4 @@ function _page($$renderer, $$props) {
35942
36117
  }
35943
36118
 
35944
36119
  export { _page as default };
35945
- //# sourceMappingURL=_page.svelte.js-CKY8hz9T.js.map
36120
+ //# sourceMappingURL=_page.svelte.js-7xWLRsiZ.js.map