@deepseek-insight/dsi 0.8.4 → 0.8.5

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 (109) hide show
  1. package/.agents/skills/dsi-gb-verifier/SKILL.md +143 -0
  2. package/build/client/_app/immutable/assets/0.C6AGVUc8.css +2 -0
  3. package/build/client/_app/immutable/assets/{2.BPprd30B.css → 2.gXzZuuwh.css} +1 -1
  4. package/build/client/_app/immutable/chunks/BFOY8uwF.js +1 -0
  5. package/build/client/_app/immutable/chunks/CrTRqozz.js +1 -0
  6. package/build/client/_app/immutable/chunks/DxGYLnDH.js +1 -0
  7. package/build/client/_app/immutable/chunks/HD-mJtOS.js +6 -0
  8. package/build/client/_app/immutable/chunks/{CnzmIbjr.js → b4-Ro8NH.js} +1 -1
  9. package/build/client/_app/immutable/chunks/hC0DBYN6.js +2 -0
  10. package/build/client/_app/immutable/entry/{app.DYNedUxy.js → app.DHewwbUQ.js} +2 -2
  11. package/build/client/_app/immutable/entry/start.C0PcwkeZ.js +1 -0
  12. package/build/client/_app/immutable/nodes/{0.DQYP6BCS.js → 0.CP10wY64.js} +1 -1
  13. package/build/client/_app/immutable/nodes/{1.CHB5qqQ9.js → 1.qWvE0DiU.js} +1 -1
  14. package/build/client/_app/immutable/nodes/{2.C6No1310.js → 2.Nw7iFtoh.js} +224 -223
  15. package/build/client/_app/version.json +1 -1
  16. package/build/handler.js +4 -4
  17. package/build/index.js +4 -4
  18. package/build/server/chunks/chunks/{app-config.svelte.js-BM4WbMFB.js → app-config.svelte.js-DCc9d7A4.js} +6 -2
  19. package/build/server/chunks/chunks/{config.js-vThvQeMn.js → config.js-s6iKwHD7.js} +3 -2
  20. package/build/server/chunks/chunks/{db.js-DQm6CZ_r.js → db.js-CxxWK5sn.js} +2 -2
  21. package/build/server/chunks/chunks/{db2.js-DUhFxA3a.js → db2.js-DwJ9lySe.js} +2 -2
  22. package/build/server/chunks/chunks/{dsh-connection.js-BgeraFA2.js → dsh-connection.js-Chtudg1p.js} +3 -3
  23. package/build/server/chunks/chunks/{dsh-events.js-BUkLR6ZV.js → dsh-events.js-BUdvZQmd.js} +6 -3
  24. package/build/server/chunks/chunks/{git-probe.js-CLF37SRh.js → git-probe.js-BX9FPnet.js} +3 -3
  25. package/build/server/chunks/chunks/{insight-config.js-Dp_2gB5J.js → insight-config.js-Db5fe5Df.js} +9 -5
  26. package/build/server/chunks/chunks/{internal2.js-J3h-US64.js → internal2.js-CaZaz-a9.js} +3 -3
  27. package/build/server/chunks/chunks/ledger-stats.js-BWACyDPg.js +72 -0
  28. package/build/server/chunks/chunks/{registry.js-CZNJZ0C-.js → registry.js-syaTZ_-A.js} +341 -21
  29. package/build/server/chunks/chunks/{settings-document.js-Gw1puaik.js → settings-document.js-BHESJ7im.js} +2 -2
  30. package/build/server/chunks/chunks/{state.js-8RBD56dL.js → state.js-oYjf745s.js} +3 -3
  31. package/build/server/chunks/chunks/{watcher.js-BVKnoQUw.js → watcher.js-Ci9jsM5l.js} +5 -5
  32. package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-BjzdVwdY.js → _server.ts.js-CHaYhzjy.js} +5 -5
  33. package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-3kQCQuHT.js → _server.ts.js-CdyqA85c.js} +8 -8
  34. package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-BUBTCLFn.js → _server.ts.js-DO_FS5b4.js} +8 -8
  35. package/build/server/chunks/entries/endpoints/api/browser/_id_/activate/{_server.ts.js-BdMJzENm.js → _server.ts.js-D7Be4JUf.js} +5 -5
  36. package/build/server/chunks/entries/endpoints/api/browser/_id_/close/{_server.ts.js-B5z9-N8Y.js → _server.ts.js-CGkm2H-U.js} +5 -5
  37. package/build/server/chunks/entries/endpoints/api/browser/_id_/closetab/{_server.ts.js-CnZHun5r.js → _server.ts.js-D7SLUfOo.js} +5 -5
  38. package/build/server/chunks/entries/endpoints/api/browser/_id_/element/{_server.ts.js-C1oM2Ihf.js → _server.ts.js-C3d_JV-q.js} +5 -5
  39. package/build/server/chunks/entries/endpoints/api/browser/_id_/input/{_server.ts.js-DmbqGRMg.js → _server.ts.js-DW0hyVML.js} +5 -5
  40. package/build/server/chunks/entries/endpoints/api/browser/_id_/mcp/{_server.ts.js-Cj80T1yH.js → _server.ts.js-Ev-hdBlf.js} +5 -5
  41. package/build/server/chunks/entries/endpoints/api/browser/_id_/navigate/{_server.ts.js-6gi1UYTJ.js → _server.ts.js-DZ8-wocV.js} +5 -5
  42. package/build/server/chunks/entries/endpoints/api/browser/_id_/opentab/{_server.ts.js-Bhx_J7ot.js → _server.ts.js-aapPrIQK.js} +5 -5
  43. package/build/server/chunks/entries/endpoints/api/browser/_id_/page/{_server.ts.js-DvQXNnX9.js → _server.ts.js-DANA_hMd.js} +5 -5
  44. package/build/server/chunks/entries/endpoints/api/browser/_id_/stream/{_server.ts.js-MflkVWUn.js → _server.ts.js-BqfseXFy.js} +26 -9
  45. package/build/server/chunks/entries/endpoints/api/browser/_id_/tabs/{_server.ts.js-CoUSlLcP.js → _server.ts.js-DFFF9qe-.js} +5 -5
  46. package/build/server/chunks/entries/endpoints/api/browser/_id_/viewport/{_server.ts.js-DNk4EgjJ.js → _server.ts.js-ek6-D4Zm.js} +5 -5
  47. package/build/server/chunks/entries/endpoints/api/browser/{_server.ts.js-CECKap7O.js → _server.ts.js-CPjKy3ma.js} +5 -5
  48. package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-C8XDnDwe.js → _server.ts.js-CWE9NP6g.js} +12 -5
  49. package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-BFmWshpv.js → _server.ts.js-D-raYQon.js} +5 -5
  50. package/build/server/chunks/entries/endpoints/api/dsh/jobs/{_server.ts.js-DATWy5mQ.js → _server.ts.js-CI4dEHFY.js} +5 -5
  51. package/build/server/chunks/entries/endpoints/api/dsh/jobs/follow/{_server.ts.js-CugOD1Zd.js → _server.ts.js-cnjoSLtY.js} +5 -5
  52. package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-DCupM4da.js → _server.ts.js-0UUtapX5.js} +5 -5
  53. package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-Bh1btvr9.js → _server.ts.js-MK5ezU8u.js} +5 -5
  54. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/{_server.ts.js-Cb8SBWVw.js → _server.ts.js-CEiMgzjC.js} +5 -5
  55. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-WI9yH2iG.js → _server.ts.js-wGEl1fuF.js} +5 -5
  56. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-bN9adUqz.js → _server.ts.js-Bz7u2BlD.js} +5 -5
  57. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-Bxlm966G.js → _server.ts.js-yue5aEjr.js} +5 -5
  58. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-CM-raN_Q.js → _server.ts.js-BFSs5T2m.js} +5 -5
  59. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-BzP30TXW.js → _server.ts.js-C2i85wQy.js} +7 -70
  60. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-DvNkrTaY.js → _server.ts.js-_toO4f4W.js} +5 -5
  61. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-DXbCrefw.js → _server.ts.js-BGJnDV85.js} +5 -5
  62. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-B7YRQhuJ.js → _server.ts.js-CQCJg8U8.js} +6 -6
  63. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-C_AEaLLJ.js → _server.ts.js-0ADvdE79.js} +5 -5
  64. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-Dj-pF5ik.js → _server.ts.js-DyO4T1Ro.js} +4 -4
  65. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-BX9FWJ6g.js → _server.ts.js-CIY7oimw.js} +5 -5
  66. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-CQ2Bmtu4.js → _server.ts.js-B-9faNHX.js} +5 -5
  67. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-Ha7xjnKW.js → _server.ts.js-E7-0zfiH.js} +5 -5
  68. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-BOg6llHk.js → _server.ts.js-CODtLQ3Q.js} +5 -5
  69. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-DBqGALzZ.js → _server.ts.js-BiSxsU-j.js} +5 -5
  70. package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-CvA8WDDb.js → _server.ts.js-C0WJnv2b.js} +5 -5
  71. package/build/server/chunks/entries/endpoints/api/dsh/subagents/_server.ts.js-D5bc4xg4.js +72 -0
  72. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-BfjNH40j.js → _server.ts.js-23ycAyUg.js} +5 -5
  73. package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-De_qumyr.js → _server.ts.js-B4w5Jh7f.js} +5 -5
  74. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-tFpJJHCb.js → _server.ts.js-VQLxDbFE.js} +5 -5
  75. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-DVx5GGA_.js → _server.ts.js-vs5N5Cya.js} +5 -5
  76. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-DuKT-aZp.js → _server.ts.js-BOWhoBHV.js} +5 -5
  77. package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-DsWFidFY.js → _server.ts.js-aDEAGfJe.js} +5 -5
  78. package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-C6j0adQs.js → _server.ts.js-BZ-nRrTp.js} +5 -5
  79. package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-BChAg4hm.js → _server.ts.js-CkDY1PQ7.js} +5 -5
  80. package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-BPSHqbZN.js → _server.ts.js-CHvgF47Y.js} +4 -4
  81. package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-D5CbUe5k.js → _server.ts.js-CbRT4vBo.js} +4 -4
  82. package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-B-5YihUA.js → _server.ts.js-BwD0KaXy.js} +7 -7
  83. package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-DT5jRZFE.js → _server.ts.js-DgbMRh3d.js} +7 -7
  84. package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-DtW-utrd.js → _server.ts.js-DRqZawic.js} +7 -7
  85. package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-8AIAo9jX.js → _server.ts.js-CTXwwwB1.js} +7 -7
  86. package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-CMw96vjY.js → _server.ts.js-B4aJBY2w.js} +7 -7
  87. package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-DKufa14_.js → _server.ts.js-CDJJA18L.js} +7 -7
  88. package/build/server/chunks/entries/{hooks.server.js-B-e4_ekh.js → hooks.server.js-Iel6oE7w.js} +3 -3
  89. package/build/server/chunks/entries/pages/{_error.svelte.js-DRPduJdP.js → _error.svelte.js-B-wdFjVG.js} +4 -4
  90. package/build/server/chunks/entries/pages/{_page.server.ts.js-7x6ZfGoB.js → _page.server.ts.js-CaoeQzem.js} +3 -3
  91. package/build/server/chunks/entries/pages/{_page.svelte.js-Dek8Q1_v.js → _page.svelte.js-CY7zcWr4.js} +1480 -1062
  92. package/build/server/chunks/{handler-DUyvzXsb.js → handler-DYk4NJto.js} +3 -3
  93. package/build/server/chunks/{index.js-BrP1SRzN.js → index.js-kL9MZ99b.js} +2 -2
  94. package/build/server/chunks/{manifest.js-C4z6aoYx.js → manifest.js-BrMLQqec.js} +67 -60
  95. package/build/server/chunks/nodes/{0.js-BuB3t8Br.js → 0.js-BembYTTr.js} +3 -3
  96. package/build/server/chunks/nodes/1.js-CKJjyWGZ.js +9 -0
  97. package/build/server/chunks/nodes/{2.js-C-o9KF10.js → 2.js-BezA2RPm.js} +10 -10
  98. package/package.json +1 -1
  99. package/skills/{dsi-kb-scenario → dsi-fc-scenario}/SKILL.md +11 -6
  100. package/template/.dsi/prompts.sqlite-shm +0 -0
  101. package/template/.dsi/prompts.sqlite-wal +0 -0
  102. package/build/client/_app/immutable/assets/0.cswkTiBk.css +0 -2
  103. package/build/client/_app/immutable/chunks/C7Sg_49n.js +0 -1
  104. package/build/client/_app/immutable/chunks/D-Rolhzs.js +0 -1
  105. package/build/client/_app/immutable/chunks/DY0MQ1mO.js +0 -1
  106. package/build/client/_app/immutable/chunks/GKkjunMm.js +0 -2
  107. package/build/client/_app/immutable/chunks/NcOjFWr3.js +0 -6
  108. package/build/client/_app/immutable/entry/start.BzyrBssE.js +0 -1
  109. package/build/server/chunks/nodes/1.js-CW4V8ni8.js +0 -9
@@ -1,15 +1,15 @@
1
1
  import { a as __exportAll, o as onDestroy } from '../../chunks/index-server.js-DmIEbDVz.js';
2
2
  import '../../chunks/exports.js-DH_tjtdv.js';
3
3
  import { M as derived, V as head, T as writable, W as escape_html, X as attr, Y as bind_props, a as setContext, Z as attr_class, _ as attr_style, $ as store_get, a0 as spread_props, a1 as stringify, a2 as attributes, a3 as clsx, a4 as ensure_array_like, a5 as element, g as getContext, a6 as unsubscribe_stores, a7 as html, r as run, a8 as props_id } from '../../chunks/server.js-B1bDXhHu.js';
4
- import { U as UI_LOCALES } from '../../chunks/config.js-vThvQeMn.js';
4
+ import { U as UI_LOCALES } from '../../chunks/config.js-s6iKwHD7.js';
5
5
  import { m as mergeEntries, c as collapseSystemPromptEntries, t as truncate } from '../../chunks/merge.js-yMwW440S.js';
6
6
  import { p as parseA2aSignature, m as mintA2aId, b as buildDeliveredText } from '../../chunks/a2a-protocol.js-C77mVg2Q.js';
7
7
  import { b as applyKnobEvent, E as EMPTY_KNOBS, r as resolvePermission, l as localPermission, c as presetLabel, d as presetDescription, C as CUSTOM_PRESET, F as FULL_ACCESS_PRESET } from '../../chunks/permission-state.js-B7UpAHBv.js';
8
8
  import { D as DEFAULT_IMAGE_LIMITS } from '../../chunks/image-limits.js-CUdJ0f7r.js';
9
9
  import { s as sanitizeWorkspaceProfile, d as dsiKey, t as themePrefs } from '../../chunks/theme-prefs.svelte.js-8CaWz46g.js';
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';
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-DCc9d7A4.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-8RBD56dL.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-oYjf745s.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-J3h-US64.js';
24
+ import '../../chunks/internal2.js-CaZaz-a9.js';
25
25
  import '../../chunks/utils.js-CAdNO8Tu.js';
26
26
 
27
27
  //#region src/lib/paraglide/messages/abort.js
@@ -1208,6 +1208,38 @@ var browserlaunching1 = ((inputs = {}, options = {}) => {
1208
1208
  return en_browserlaunching1();
1209
1209
  });
1210
1210
  //#endregion
1211
+ //#region src/lib/paraglide/messages/browsernewtab2.js
1212
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1213
+ /** @typedef {{}} Browsernewtab2Inputs */
1214
+ var en_browsernewtab2 = () => {
1215
+ return `New tab`;
1216
+ };
1217
+ var zh_browsernewtab2 = () => {
1218
+ return `新标签页`;
1219
+ };
1220
+ var id_browsernewtab2 = () => {
1221
+ return `Tab baru`;
1222
+ };
1223
+ var es_browsernewtab2 = () => {
1224
+ return `Nueva pestaña`;
1225
+ };
1226
+ /**
1227
+ * | output |
1228
+ * | --- |
1229
+ * | "New tab" |
1230
+ *
1231
+ * @param {Browsernewtab2Inputs} inputs
1232
+ * @param {{ locale?: "en" | "zh" | "id" | "es" }} options
1233
+ * @returns {LocalizedString}
1234
+ */
1235
+ var browsernewtab2 = ((inputs = {}, options = {}) => {
1236
+ const locale = options.locale ?? getLocale();
1237
+ if (locale === "zh") return zh_browsernewtab2();
1238
+ if (locale === "id") return id_browsernewtab2();
1239
+ if (locale === "es") return es_browsernewtab2();
1240
+ return en_browsernewtab2();
1241
+ });
1242
+ //#endregion
1211
1243
  //#region src/lib/paraglide/messages/browseropentitle2.js
1212
1244
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1213
1245
  /** @typedef {{}} Browseropentitle2Inputs */
@@ -12568,6 +12600,70 @@ var streaming = ((inputs = {}, options = {}) => {
12568
12600
  return en_streaming();
12569
12601
  });
12570
12602
  //#endregion
12603
+ //#region src/lib/paraglide/messages/subagentanchorlabel2.js
12604
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
12605
+ /** @typedef {{}} Subagentanchorlabel2Inputs */
12606
+ var en_subagentanchorlabel2 = () => {
12607
+ return `Subagents`;
12608
+ };
12609
+ var zh_subagentanchorlabel2 = () => {
12610
+ return `子代理`;
12611
+ };
12612
+ var id_subagentanchorlabel2 = () => {
12613
+ return `Subagen`;
12614
+ };
12615
+ var es_subagentanchorlabel2 = () => {
12616
+ return `Subagentes`;
12617
+ };
12618
+ /**
12619
+ * | output |
12620
+ * | --- |
12621
+ * | "Subagents" |
12622
+ *
12623
+ * @param {Subagentanchorlabel2Inputs} inputs
12624
+ * @param {{ locale?: "en" | "zh" | "id" | "es" }} options
12625
+ * @returns {LocalizedString}
12626
+ */
12627
+ var subagentanchorlabel2 = ((inputs = {}, options = {}) => {
12628
+ const locale = options.locale ?? getLocale();
12629
+ if (locale === "zh") return zh_subagentanchorlabel2();
12630
+ if (locale === "id") return id_subagentanchorlabel2();
12631
+ if (locale === "es") return es_subagentanchorlabel2();
12632
+ return en_subagentanchorlabel2();
12633
+ });
12634
+ //#endregion
12635
+ //#region src/lib/paraglide/messages/subagentcloseallpanels3.js
12636
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
12637
+ /** @typedef {{}} Subagentcloseallpanels3Inputs */
12638
+ var en_subagentcloseallpanels3 = () => {
12639
+ return `del panel(s)`;
12640
+ };
12641
+ var zh_subagentcloseallpanels3 = () => {
12642
+ return `删除面板`;
12643
+ };
12644
+ var id_subagentcloseallpanels3 = () => {
12645
+ return `hapus panel`;
12646
+ };
12647
+ var es_subagentcloseallpanels3 = () => {
12648
+ return `eliminar panel(es)`;
12649
+ };
12650
+ /**
12651
+ * | output |
12652
+ * | --- |
12653
+ * | "del panel(s)" |
12654
+ *
12655
+ * @param {Subagentcloseallpanels3Inputs} inputs
12656
+ * @param {{ locale?: "en" | "zh" | "id" | "es" }} options
12657
+ * @returns {LocalizedString}
12658
+ */
12659
+ var subagentcloseallpanels3 = ((inputs = {}, options = {}) => {
12660
+ const locale = options.locale ?? getLocale();
12661
+ if (locale === "zh") return zh_subagentcloseallpanels3();
12662
+ if (locale === "id") return id_subagentcloseallpanels3();
12663
+ if (locale === "es") return es_subagentcloseallpanels3();
12664
+ return en_subagentcloseallpanels3();
12665
+ });
12666
+ //#endregion
12571
12667
  //#region src/lib/paraglide/messages/subagentmany1.js
12572
12668
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
12573
12669
  /** @typedef {{ n: NonNullable<unknown> }} Subagentmany1Inputs */
@@ -13688,6 +13784,38 @@ var tooltitlewrite2 = ((inputs = {}, options = {}) => {
13688
13784
  return en_tooltitlewrite2();
13689
13785
  });
13690
13786
  //#endregion
13787
+ //#region src/lib/paraglide/messages/totaltokens1.js
13788
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
13789
+ /** @typedef {{}} Totaltokens1Inputs */
13790
+ var en_totaltokens1 = () => {
13791
+ return `Total tokens`;
13792
+ };
13793
+ var zh_totaltokens1 = () => {
13794
+ return `总 Token 数`;
13795
+ };
13796
+ var id_totaltokens1 = () => {
13797
+ return `Total token`;
13798
+ };
13799
+ var es_totaltokens1 = () => {
13800
+ return `Tokens totales`;
13801
+ };
13802
+ /**
13803
+ * | output |
13804
+ * | --- |
13805
+ * | "Total tokens" |
13806
+ *
13807
+ * @param {Totaltokens1Inputs} inputs
13808
+ * @param {{ locale?: "en" | "zh" | "id" | "es" }} options
13809
+ * @returns {LocalizedString}
13810
+ */
13811
+ var totaltokens1 = ((inputs = {}, options = {}) => {
13812
+ const locale = options.locale ?? getLocale();
13813
+ if (locale === "zh") return zh_totaltokens1();
13814
+ if (locale === "id") return id_totaltokens1();
13815
+ if (locale === "es") return es_totaltokens1();
13816
+ return en_totaltokens1();
13817
+ });
13818
+ //#endregion
13691
13819
  //#region src/lib/paraglide/messages/turnfailed1.js
13692
13820
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
13693
13821
  /** @typedef {{}} Turnfailed1Inputs */
@@ -15072,6 +15200,7 @@ var _index_exports = /* @__PURE__ */ __exportAll({
15072
15200
  browserCtxSendRightClick: () => browserctxsendrightclick4,
15073
15201
  browserForward: () => browserforward1,
15074
15202
  browserLaunching: () => browserlaunching1,
15203
+ browserNewTab: () => browsernewtab2,
15075
15204
  browserOpenTitle: () => browseropentitle2,
15076
15205
  browserPanelTitle: () => browserpaneltitle2,
15077
15206
  browserReload: () => browserreload1,
@@ -15430,6 +15559,8 @@ var _index_exports = /* @__PURE__ */ __exportAll({
15430
15559
  stop: () => stop,
15431
15560
  stopStreaming: () => stopstreaming1,
15432
15561
  streaming: () => streaming,
15562
+ subagentAnchorLabel: () => subagentanchorlabel2,
15563
+ subagentCloseAllPanels: () => subagentcloseallpanels3,
15433
15564
  subagentMany: () => subagentmany1,
15434
15565
  subagentOne: () => subagentone1,
15435
15566
  subagentTitleOwns: () => subagenttitleowns2,
@@ -15465,6 +15596,7 @@ var _index_exports = /* @__PURE__ */ __exportAll({
15465
15596
  toolTitleSearch: () => tooltitlesearch2,
15466
15597
  toolTitleToolCall: () => tooltitletoolcall3,
15467
15598
  toolTitleWrite: () => tooltitlewrite2,
15599
+ totalTokens: () => totaltokens1,
15468
15600
  turnFailed: () => turnfailed1,
15469
15601
  turnInFlight: () => turninflight2,
15470
15602
  turnStopped: () => turnstopped1,
@@ -15550,6 +15682,7 @@ var messages_exports = /* @__PURE__ */ __exportAll({
15550
15682
  browserCtxSendRightClick: () => browserctxsendrightclick4,
15551
15683
  browserForward: () => browserforward1,
15552
15684
  browserLaunching: () => browserlaunching1,
15685
+ browserNewTab: () => browsernewtab2,
15553
15686
  browserOpenTitle: () => browseropentitle2,
15554
15687
  browserPanelTitle: () => browserpaneltitle2,
15555
15688
  browserReload: () => browserreload1,
@@ -15909,6 +16042,8 @@ var messages_exports = /* @__PURE__ */ __exportAll({
15909
16042
  stop: () => stop,
15910
16043
  stopStreaming: () => stopstreaming1,
15911
16044
  streaming: () => streaming,
16045
+ subagentAnchorLabel: () => subagentanchorlabel2,
16046
+ subagentCloseAllPanels: () => subagentcloseallpanels3,
15912
16047
  subagentMany: () => subagentmany1,
15913
16048
  subagentOne: () => subagentone1,
15914
16049
  subagentTitleOwns: () => subagenttitleowns2,
@@ -15944,6 +16079,7 @@ var messages_exports = /* @__PURE__ */ __exportAll({
15944
16079
  toolTitleSearch: () => tooltitlesearch2,
15945
16080
  toolTitleToolCall: () => tooltitletoolcall3,
15946
16081
  toolTitleWrite: () => tooltitlewrite2,
16082
+ totalTokens: () => totaltokens1,
15947
16083
  turnFailed: () => turnfailed1,
15948
16084
  turnInFlight: () => turninflight2,
15949
16085
  turnStopped: () => turnstopped1,
@@ -16756,6 +16892,22 @@ function Target($$renderer, $$props) {
16756
16892
  ]));
16757
16893
  }
16758
16894
  //#endregion
16895
+ //#region node_modules/.pnpm/@lucide+svelte@1.35.0_svelte@5.56.10/node_modules/@lucide/svelte/dist/icons/trash-2.svelte
16896
+ function Trash_2($$renderer, $$props) {
16897
+ let { $$slots, $$events, ...props } = $$props;
16898
+ Icon($$renderer, spread_props([
16899
+ { name: "trash-2" },
16900
+ props,
16901
+ { iconNode: [
16902
+ ["path", { "d": "M10 11v6" }],
16903
+ ["path", { "d": "M14 11v6" }],
16904
+ ["path", { "d": "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }],
16905
+ ["path", { "d": "M3 6h18" }],
16906
+ ["path", { "d": "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }]
16907
+ ] }
16908
+ ]));
16909
+ }
16910
+ //#endregion
16759
16911
  //#region node_modules/.pnpm/@lucide+svelte@1.35.0_svelte@5.56.10/node_modules/@lucide/svelte/dist/icons/wrench.svelte
16760
16912
  function Wrench($$renderer, $$props) {
16761
16913
  let { $$slots, $$events, ...props } = $$props;
@@ -17823,7 +17975,7 @@ function ThemeToggle($$renderer, $$props) {
17823
17975
  }
17824
17976
  //#endregion
17825
17977
  //#region package.json
17826
- var version = "0.8.4";
17978
+ var version = "0.8.5";
17827
17979
  var dsh = { "webVersion": "0.2.1-rc.3" };
17828
17980
  //#endregion
17829
17981
  //#region src/lib/version.ts
@@ -19515,6 +19667,10 @@ function clampPanelWidth(width, kind) {
19515
19667
  const { minWidth, maxWidth } = appConfig().panel;
19516
19668
  if (kind === "workspace-explorer") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
19517
19669
  if (kind === "workspace-file") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
19670
+ if (kind === "browser") {
19671
+ const { maxWidth: browserMax } = appConfig().browserWidths;
19672
+ return Math.max(minWidth, Math.min(browserMax, width));
19673
+ }
19518
19674
  return Math.max(minWidth, Math.min(maxWidth, width));
19519
19675
  }
19520
19676
  /** The single zoom bound site — load-side sanitizing and the zoom slider. */
@@ -19526,6 +19682,11 @@ function clampPanelZoom(zoom) {
19526
19682
  function defaultPanelWidth() {
19527
19683
  return clampPanelWidth(appConfig().panel.defaultWidth);
19528
19684
  }
19685
+ /** The glass panel's config-driven default (browser.defaultWidth, clamped
19686
+ * through the browser band) — chip-spawned glasses mint at this width. */
19687
+ function browserPanelWidth() {
19688
+ return clampPanelWidth(appConfig().browserWidths.defaultWidth, "browser");
19689
+ }
19529
19690
  /**
19530
19691
  * The skill shelf's born width (The Shelf Chrome ADR, D5): a name list,
19531
19692
  * not a five-column table — the shelf pins 480 (DEFAULT_PANEL_MIN_WIDTH)
@@ -23129,7 +23290,7 @@ function blockBounds(panels, idx, facts) {
23129
23290
  while (i > 0 && panels[i - 1].kind === "browser") i--;
23130
23291
  const head = panels[i - 1];
23131
23292
  const inFamily = (id, root) => {
23132
- if (id === void 0) return false;
23293
+ if (id === void 0 || root === void 0) return false;
23133
23294
  let cur = id;
23134
23295
  for (let guard = 0; guard < panels.length + 1 && cur !== null; guard++) {
23135
23296
  if (cur === root) return true;
@@ -23150,6 +23311,7 @@ function blockBounds(panels, idx, facts) {
23150
23311
  sessionId = head.sessionId;
23151
23312
  }
23152
23313
  }
23314
+ if (sessionId === null) return [idx, idx];
23153
23315
  for (let guard = 0; guard < panels.length + 1; guard++) {
23154
23316
  const parent = facts.parentOf.get(sessionId) ?? null;
23155
23317
  if (parent === null) break;
@@ -23485,7 +23647,7 @@ function panelRowsFor(input) {
23485
23647
  }
23486
23648
  if (p.kind === "terminal" || p.kind === "prompt-manager" || p.kind === "settings-editor" || p.kind === "skill-shelf" || p.kind === "plugin-rack") return {
23487
23649
  panel: p,
23488
- title: p.kind === "terminal" ? "Terminal" : p.kind === "browser" ? "Browser" : p.kind === "prompt-manager" ? MANAGER_ROW_TITLE : p.kind === "skill-shelf" ? "Skill Shelf" : p.kind === "plugin-rack" ? "Plugin Rack" : p.kind === "settings-editor" ? SETTINGS_ROW_TITLE[p.target] : "",
23650
+ title: p.kind === "terminal" ? "Terminal" : p.kind === "prompt-manager" ? MANAGER_ROW_TITLE : p.kind === "skill-shelf" ? "Skill Shelf" : p.kind === "plugin-rack" ? "Plugin Rack" : p.kind === "settings-editor" ? SETTINGS_ROW_TITLE[p.target] : "",
23489
23651
  workspace: null,
23490
23652
  dead: false,
23491
23653
  running: false,
@@ -24928,7 +25090,7 @@ function ConversationHeader($$renderer, $$props) {
24928
25090
  * upward.
24929
25091
  */
24930
25092
  let { sessionId, title, workspace = null, workspaces = [], access = null, agent, agentId = null, isStreaming, transcript = null, subagent = false, plan = null, parentSessionId = null, focused = false, onOpenExplorer = void 0, onOpenBrowser = void 0 } = $$props;
24931
- $$renderer.push(`<header data-svelte="ConversationHeader"${attr_class(`flex items-center gap-3 border-b border-surface-border px-2 py-1 ${focused ? "bg-header-wash" : "bg-surface-elevated"}`)} data-testid="conversation-header"${attr("data-focused", focused ? "true" : "false")}>`);
25093
+ $$renderer.push(`<header data-svelte="ConversationHeader"${attr_class(`flex items-center gap-1 border-b border-surface-border px-2 py-1 ${focused ? "bg-header-wash" : "bg-surface-elevated"}`)} data-testid="conversation-header"${attr("data-focused", focused ? "true" : "false")}>`);
24932
25094
  SessionIdAndName($$renderer, {
24933
25095
  sessionId,
24934
25096
  title,
@@ -31031,13 +31193,13 @@ function FloatingAnchorContainerPopup($$renderer, $$props) {
31031
31193
  * `titleTestId` exists because the header's data-testid is part of
31032
31194
  * each leaf's pinned surface (plan-popup-header, user-message-jumper-header).
31033
31195
  */
31034
- let { open = false, triggerEl = void 0, title = void 0, titleTestId = void 0, popupTestId = "floating-anchor-popup", fitWidth = false, children } = $$props;
31196
+ let { open = false, triggerEl = void 0, title = void 0, titleTestId = void 0, popupTestId = "floating-anchor-popup", fitWidth = false, placement = "left", children } = $$props;
31035
31197
  /** Focus the box — leaves call it through bind:this when an action
31036
31198
  * must hand focus back (the jumper's stay-open jump). */
31037
31199
  function focus() {}
31038
31200
  if (open) {
31039
31201
  $$renderer.push("<!--[0-->");
31040
- $$renderer.push(`<div data-svelte="FloatingAnchorContainerPopup" tabindex="-1"${attr_class(`pointer-events-auto absolute right-full top-1/2 z-50 mr-2 max-h-[60vh] max-w-[80vw] -translate-y-1/2 overflow-y-auto rounded-lg border border-surface-border bg-surface-elevated py-1 shadow-xl outline-none focus:outline-none ${fitWidth ? "w-fit" : "w-96"}`)}${attr("data-testid", popupTestId)}>`);
31202
+ $$renderer.push(`<div data-svelte="FloatingAnchorContainerPopup" tabindex="-1"${attr_class(`pointer-events-auto absolute ${placement === "right" ? "left-full ml-2" : placement === "below" ? "right-0 top-full mt-2" : "right-full mr-2"} ${placement === "below" ? "" : "top-1/2 -translate-y-1/2"} z-50 max-h-[60vh] max-w-[80vw] overflow-y-auto rounded-lg border border-surface-border bg-surface-elevated py-1 shadow-xl outline-none focus:outline-none ${fitWidth ? "w-fit" : "w-96"}`)}${attr("data-testid", popupTestId)}>`);
31041
31203
  if (title !== void 0) {
31042
31204
  $$renderer.push("<!--[0-->");
31043
31205
  $$renderer.push(`<div class="border-b border-surface-border px-2.5 py-1.5 text-[10px] font-medium uppercase tracking-wider text-text-muted"${attr("data-testid", titleTestId)}>${escape_html(title)}</div>`);
@@ -31653,346 +31815,1101 @@ function InjectedShelfButton($$renderer, $$props) {
31653
31815
  });
31654
31816
  }
31655
31817
  //#endregion
31656
- //#region src/lib/components/conversation/panel/ConversationFloatingAnchor.svelte
31657
- function ConversationFloatingAnchor($$renderer, $$props) {
31658
- /**
31659
- * ConversationFloatingAnchor — the conversation page's floating action
31660
- * stack (OCI Floating Host port, 2026-08-23; extracted from the page
31661
- * 2026-08-24): jump and scroll actions beside the transcript they act
31662
- * on — the User Messages button (self-contained with its popup jumper
31663
- * since 2026-08-26; renders only when prompt groups exist) and the
31664
- * far-edge jump. Purely presentational — the viewport and turn groups
31665
- * arrive as props.
31666
- */
31667
- let { groups, container, todos = null, deliverables = null, onfileopen = void 0, jobsessionid = "", shelfentries = void 0, shelfsessionid = "", shelfworkspace = null, shelfpanelid = null, onshelfnote } = $$props;
31668
- if (container !== void 0) {
31669
- $$renderer.push("<!--[0-->");
31670
- FloatingAnchor($$renderer, {
31671
- children: ($$renderer) => {
31672
- PlanButton($$renderer, { todos: todos ?? [] });
31673
- $$renderer.push(`<!----> `);
31674
- DeliverablesButton($$renderer, {
31675
- deliveries: deliverables,
31676
- onfileopen
31677
- });
31678
- $$renderer.push(`<!----> `);
31679
- JobsButton($$renderer, { sessionId: jobsessionid });
31680
- $$renderer.push(`<!----> `);
31681
- if (shelfentries !== void 0) {
31682
- $$renderer.push("<!--[0-->");
31683
- InjectedShelfButton($$renderer, {
31684
- entries: shelfentries,
31685
- sessionId: shelfsessionid,
31686
- workspace: shelfworkspace,
31687
- panelId: shelfpanelid,
31688
- onnote: onshelfnote
31689
- });
31690
- } else $$renderer.push("<!--[-1-->");
31691
- $$renderer.push(`<!--]--> `);
31692
- UserMessagesButton($$renderer, {
31693
- groups,
31694
- container
31695
- });
31696
- $$renderer.push(`<!----> `);
31697
- BackToTheEdgeButton($$renderer);
31698
- $$renderer.push(`<!---->`);
31699
- }});
31700
- } else $$renderer.push("<!--[-1-->");
31701
- $$renderer.push(`<!--]-->`);
31702
- }
31703
- //#endregion
31704
- //#region src/lib/components/conversation/panel/A2aChipStack.svelte
31705
- function A2aChipStack($$renderer, $$props) {
31706
- $$renderer.component(($$renderer) => {
31707
- /**
31708
- * A2aChipStack — the sender panel's read-only delegation-ledger join
31709
- * (W4 4.2): one floating chip per recent exchange THIS panel sent,
31710
- * rendered beside the transcript on the floating-anchor pattern
31711
- * (right-middle stack, per-panel via the embedded anchor host).
31712
- *
31713
- * Honesty contract (PRD risk "Attribution overclaim in UI" + spec-check
31714
- * GAP-5): chip text derives from the row's `state` VERBATIM, all six
31715
- * states distinguished — `replied_approx` renders "may have replied",
31716
- * NEVER the confident "replied" string. The ledger is the truth; this
31717
- * component claims nothing the row does not say.
31718
- *
31719
- * Read-only by construction (BC-2): rows arrive as a prop from the
31720
- * page's EXISTING spine-cadence poll of GET /api/a2a?from= — the chip
31721
- * owns NO fetch, NO timer. Click = the existing panel doorway
31722
- * (addPanelFromSidebar dedupe→select). Dismiss is LOCAL-ONLY visual:
31723
- * rows are durable; the next poll brings a dismissed chip back only
31724
- * when it is still in the page's fetched window.
31725
- */
31726
- let { rows, titles } = $$props;
31727
- /** Local-only visual dismissal — rows are durable; the ledger is truth. */
31728
- let dismissed = /* @__PURE__ */ new Set();
31729
- /** The cap: newest 3 visible + one overflow indicator (spec 4.2). */
31730
- const CHIP_CAP = 3;
31731
- const visible = derived(() => rows.filter((r) => !dismissed.has(r.id)));
31732
- const capped = derived(() => visible().slice(0, CHIP_CAP));
31733
- const overflow = derived(() => Math.max(visible().length - CHIP_CAP, 0));
31734
- /**
31735
- * Chip text derives from the state VERBATIM (spec-check GAP-5): every
31736
- * state distinguished, `replied_approx` hedged as "may have replied" —
31737
- * never a confident claim the row cannot support.
31738
- */
31739
- function labelFor(state) {
31740
- switch (state) {
31741
- case "waiting": return "waiting";
31742
- case "replied_exact": return "replied (exact)";
31743
- case "replied": return "replied";
31744
- case "replied_approx": return "may have replied";
31745
- case "timeout": return "no reply (timeout)";
31746
- case "gone": return "target gone";
31747
- }
31748
- }
31749
- /** The target's display title — the spine title, else the id prefix. */
31750
- function titleFor(toSession) {
31751
- return titles[toSession] ?? toSession.replace(/^session-/, "").slice(0, 8);
31752
- }
31753
- if (visible().length > 0) {
31754
- $$renderer.push("<!--[0-->");
31755
- FloatingAnchor($$renderer, {
31756
- children: ($$renderer) => {
31757
- $$renderer.push(`<!--[-->`);
31758
- const each_array = ensure_array_like(capped());
31759
- for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
31760
- let row = each_array[$$index];
31761
- $$renderer.push(`<div class="a2a-chip-wrap" data-testid="a2a-chip"${attr("data-a2a-state", row.state)}${attr("data-a2a-id", row.id)}><button type="button"${attr_class("a2a-chip", void 0, {
31762
- "tone-waiting": row.state === "waiting",
31763
- "tone-replied-exact": row.state === "replied_exact",
31764
- "tone-replied": row.state === "replied",
31765
- "tone-replied-approx": row.state === "replied_approx",
31766
- "tone-timeout": row.state === "timeout",
31767
- "tone-gone": row.state === "gone"
31768
- })} data-testid="a2a-chip-button"${attr("title", `${stringify(titleFor(row.toSession))} — ${stringify(labelFor(row.state))}`)}>`);
31769
- if (row.state === "waiting") {
31770
- $$renderer.push("<!--[0-->");
31771
- Hourglass($$renderer, { size: 13 });
31772
- } else if (row.state === "replied_exact" || row.state === "replied") {
31773
- $$renderer.push("<!--[1-->");
31774
- Check($$renderer, { size: 13 });
31775
- } else $$renderer.push("<!--[-1-->");
31776
- $$renderer.push(`<!--]--> <span class="a2a-chip-label">${escape_html(labelFor(row.state))}</span></button> <button type="button" class="a2a-dismiss" data-testid="a2a-chip-dismiss" title="hide this chip (the ledger row stays)">`);
31777
- X($$renderer, { size: 11 });
31778
- $$renderer.push(`<!----></button></div>`);
31779
- }
31780
- $$renderer.push(`<!--]--> `);
31781
- if (overflow() > 0) {
31782
- $$renderer.push("<!--[0-->");
31783
- $$renderer.push(`<div class="a2a-chip-overflow" data-testid="a2a-chip-overflow">+${escape_html(overflow())} more</div>`);
31784
- } else $$renderer.push("<!--[-1-->");
31785
- $$renderer.push(`<!--]-->`);
31786
- }});
31787
- } else $$renderer.push("<!--[-1-->");
31788
- $$renderer.push(`<!--]-->`);
31789
- });
31790
- }
31791
- //#endregion
31792
- //#region src/lib/components/conversation/panel/ConversationStatsBar.svelte
31793
- function ConversationStatsBar($$renderer, $$props) {
31794
- $$renderer.component(($$renderer) => {
31795
- /**
31796
- * ConversationStatsBar — the panel's session baseline (ADR 2026-09-08
31797
- * "The Stats Bar", D1/D3/D5): one fixed-height line under the transcript
31798
- * showing what the ledger already proves — turns, steps, tool time,
31799
- * token totals, cache hit. Pure presentation: the panel derives the
31800
- * stats and hands them down; segments whose inputs never reached DSI
31801
- * are omitted, never approximated. Renders nothing before the first
31802
- * assistant message (D5 gate lives in the derive's null return).
31803
- */
31804
- let { stats } = $$props;
31805
- /** 85000 -> "1m25s"; 2400000 -> "40m"; 5400000 -> "1h30m"; 250 -> "250ms". */
31806
- function formatDuration(ms) {
31807
- if (ms < 1e3) return `${ms}ms`;
31808
- const totalSec = Math.floor(ms / 1e3);
31809
- const h = Math.floor(totalSec / 3600);
31810
- const m = Math.floor(totalSec % 3600 / 60);
31811
- const s = totalSec % 60;
31812
- if (h > 0) return `${h}h${m}m`;
31813
- if (m > 0) return s > 0 ? `${m}m${s}s` : `${m}m`;
31814
- return `${s}s`;
31818
+ //#region src/lib/services/conversation/subagent-clock.svelte.ts
31819
+ var SubagentClock = class {
31820
+ /** sessionId → first-sight epoch ms (running rows only). */
31821
+ firstSeen = {};
31822
+ /** sessionId → frozen elapsed ms, set once at the settle observation. */
31823
+ frozen = {};
31824
+ /** The tick's reading — read by elapsedMs so a live tick re-renders. */
31825
+ now = Date.now();
31826
+ /** Feed one spine tick: register unseen running ids, freeze settled
31827
+ * ones, expire ids gone from the feed. Call on every fetch (the
31828
+ * button's poll cadence), not on a timer — this IS the sighting lane. */
31829
+ observe(rows) {
31830
+ const t = Date.now();
31831
+ const seen = /* @__PURE__ */ new Set();
31832
+ for (const row of rows) {
31833
+ seen.add(row.sessionId);
31834
+ if (row.running && this.firstSeen[row.sessionId] === void 0) this.firstSeen[row.sessionId] = t;
31835
+ if (!row.running && this.firstSeen[row.sessionId] !== void 0 && this.frozen[row.sessionId] === void 0) this.frozen[row.sessionId] = t - this.firstSeen[row.sessionId];
31815
31836
  }
31816
- /** 15800 -> "15.8K"; 56300 -> "56.3K"; 15800000 -> "15.8M"; 108000 -> "108K"; 999 -> "999". */
31817
- function formatTokens(n) {
31818
- if (n >= 1e6) return `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
31819
- if (n >= 1e3) return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}K`;
31820
- return String(n);
31837
+ for (const id of Object.keys(this.firstSeen)) if (!seen.has(id)) {
31838
+ delete this.firstSeen[id];
31839
+ delete this.frozen[id];
31821
31840
  }
31822
- const segments = derived(() => {
31823
- if (stats === null) return [];
31824
- const parts = [`${stats.turns} turns · ${stats.steps} steps`];
31825
- const durations = [];
31826
- if (stats.llmMs !== void 0 && stats.llmMs > 0) durations.push(`LLM ${formatDuration(stats.llmMs)}`);
31827
- if (stats.toolMs > 0) durations.push(`Tool ${formatDuration(stats.toolMs)}`);
31828
- if (durations.length > 0) parts.push(durations.join(" · "));
31829
- const speeds = [];
31830
- if (stats.ttftSteps !== void 0 && stats.ttftSteps > 0 && stats.ttftMs !== void 0) speeds.push(`TTFT avg ${formatDuration(stats.ttftMs / stats.ttftSteps)}`);
31831
- if (stats.decodeMs !== void 0 && stats.decodeMs > 0 && stats.decodeTokens !== void 0) speeds.push(`${Math.max(1, Math.round(stats.decodeTokens / (stats.decodeMs / 1e3)))} tok/s`);
31832
- if (speeds.length > 0) parts.push(speeds.join(" · "));
31833
- const inputTotal = stats.billedInputTokens ?? stats.inputTokens;
31834
- if (inputTotal !== void 0) parts.push(`Input ${formatTokens(inputTotal)} tok · Output ${formatTokens(stats.outputTokens ?? 0)} tok`);
31835
- if (stats.cacheHitPercent !== void 0) parts.push(`Cache hit ${stats.cacheHitPercent}%`);
31836
- return parts;
31837
- });
31838
- if (stats !== null) {
31839
- $$renderer.push("<!--[0-->");
31840
- $$renderer.push(`<div data-svelte="ConversationStatsBar" class="stats-bar svelte-hnxn10" data-testid="conversation-stats-bar"${attr("title", segments().join(" | "))}><!--[-->`);
31841
- const each_array = ensure_array_like(segments());
31842
- for (let i = 0, $$length = each_array.length; i < $$length; i++) {
31843
- let segment = each_array[i];
31844
- if (i > 0) {
31845
- $$renderer.push("<!--[0-->");
31846
- $$renderer.push(`<span class="sep svelte-hnxn10" aria-hidden="true">|</span>`);
31847
- } else $$renderer.push("<!--[-1-->");
31848
- $$renderer.push(`<!--]--> <span class="segment svelte-hnxn10" data-testid="stats-segment">${escape_html(segment)}</span>`);
31849
- }
31850
- $$renderer.push(`<!--]--></div>`);
31851
- } else $$renderer.push("<!--[-1-->");
31852
- $$renderer.push(`<!--]-->`);
31853
- });
31854
- }
31855
- //#endregion
31856
- //#region src/lib/components/conversation/panel/submit-ladder.svelte.ts
31857
- var SubmitLadder = class {
31858
- deps;
31859
- /** True while the plain-send rung is in flight — the footer's sending flag. */
31860
- submitted = false;
31861
- /** Inline command feedback (role=status, auto-dismiss 5s) — the ONLY
31862
- * command UI surface; the input has already cleared by intercept time.
31863
- * The wire work lives in command-executor (Prompt Macro W1 1.2): the
31864
- * /new create+swap, /permission route, and mention delivery are the
31865
- * SAME bytes the typed path always sent — the panel now renders
31866
- * outcomes only (banner kind from result.ok). */
31867
- commandNote = null;
31868
- commandNoteTimer;
31869
- constructor(deps) {
31870
- this.deps = deps;
31871
- }
31872
- /** Inline command feedback — every rung banners through here. */
31873
- noteCommand(kind, text) {
31874
- this.commandNote = {
31875
- kind,
31876
- text
31877
- };
31878
- if (this.commandNoteTimer !== void 0) clearTimeout(this.commandNoteTimer);
31879
- this.commandNoteTimer = setTimeout(() => this.commandNote = null, 5e3);
31841
+ this.now = t;
31880
31842
  }
31881
- async onsubmit(text, images) {
31882
- if (isTypedRun(text)) {
31883
- this.deps.startMacro({
31884
- text,
31885
- id: -1
31886
- }, false);
31887
- return true;
31888
- }
31889
- const command = parseCommand(text);
31890
- if (command !== null) {
31891
- let bannered = false;
31892
- const result = await executeCommand(command, this.deps.ctx(), (ok, note) => {
31893
- bannered = true;
31894
- this.noteCommand(ok ? "ok" : "error", note);
31895
- });
31896
- if (!bannered && result.note !== void 0) this.noteCommand(result.ok ? "ok" : "error", result.note);
31897
- return true;
31898
- }
31899
- if (text.trim().length === 0 && images.length === 0) return false;
31900
- const slashCatalog = this.deps.slashCatalog;
31901
- if (slashCatalog?.state === "ready") {
31902
- if (resolveHostCommand(text, slashCatalog.commands) !== null) return (await executeHostCommand(text, this.deps.ctx(), (ok, note) => this.noteCommand(ok ? "ok" : "error", note))).ok;
31903
- }
31904
- this.submitted = true;
31905
- try {
31906
- return (await sendPrompt(text, this.deps.ctx(), (t) => this.deps.orchestrator.submit(t, images))).ok;
31907
- } finally {
31908
- this.submitted = false;
31909
- }
31843
+ /** Live tick — the open popup calls this on its 1s interval so a
31844
+ * running row's elapsed advances between polls. */
31845
+ tick() {
31846
+ this.now = Date.now();
31910
31847
  }
31911
- async oncancel() {
31912
- await this.deps.orchestrator.cancel();
31848
+ /** Elapsed ms for one id: the frozen settle delta, or now − firstSeen
31849
+ * for a running row, or null when there is NO honest sample (never
31850
+ * sighted running, or first seen already settled). */
31851
+ elapsedMs(id) {
31852
+ const frozen = this.frozen[id];
31853
+ if (frozen !== void 0) return frozen;
31854
+ const t0 = this.firstSeen[id];
31855
+ if (t0 === void 0) return null;
31856
+ return Math.max(0, this.now - t0);
31913
31857
  }
31914
- /** Menu command pick: the SAME rung the ladder uses (one execution
31915
- * surface, D2), banner notes through the shared note path; the
31916
- * boolean admits the composer's draft clearing (BC-A3). */
31917
- async onpickcommand(line) {
31918
- return (await executeHostCommand(line, this.deps.ctx(), (ok, note) => this.noteCommand(ok ? "ok" : "error", note))).ok;
31858
+ /** Does the id carry any sample at all (frozen or live)? */
31859
+ sighted(id) {
31860
+ return this.frozen[id] !== void 0 || this.firstSeen[id] !== void 0;
31919
31861
  }
31920
31862
  };
31921
- //#endregion
31922
- //#region src/lib/utils/auto-open-slots.ts
31923
- /** The newest reasoning entry's id (wire order), else null — the
31924
- * transient think target. Empty reasoning strings do not count (the
31925
- * chip renders nothing to open). */
31926
- function newestReasoningId(entries) {
31927
- for (let i = entries.length - 1; i >= 0; i--) {
31928
- const e = entries[i];
31929
- if (e.kind === "assistant-message" && e.reasoning !== void 0 && e.reasoning !== "") return e.id;
31930
- }
31931
- return null;
31863
+ /** The desk-lifetime singleton — one registry per page visit (ADR D3). */
31864
+ var subagentClock = new SubagentClock();
31865
+ /**
31866
+ * The open workspace explorer for a workspace ROOT — The Shared Tree ADR
31867
+ * (2026-09-17) D1's dedupe key: ONE explorer per ROOT across ALL sessions,
31868
+ * so a chip click on any session sharing the root FOCUSES instead of
31869
+ * adding (a duplicate is a bug, not a feature). The retired per-session
31870
+ * key ("two sessions sharing one workspace hold distinct explorers",
31871
+ * Workspace Explorer D4) produced N byte-identical trees for N sessions.
31872
+ */
31873
+ function findWorkspaceExplorerPanel(panels, root) {
31874
+ return panels.find((p) => p.kind === "workspace-explorer" && p.root === root);
31932
31875
  }
31933
- /** Suppression-aware view of a raw target: null while the user kept THIS
31934
- * id collapsed (a newer target id simply stops matching). */
31935
- function suppressed(targetId, suppressedId) {
31936
- return targetId !== null && suppressedId === targetId ? null : targetId;
31876
+ /**
31877
+ * The explorer's slot (The Shared Tree ADR 2026-09-17, D3): the FOCUSED
31878
+ * slot — the explorer is NOT a lineage child; a workspace tree has no
31879
+ * parent session, only an opener. The retired after-source rule
31880
+ * (Workspace Explorer D3, "Parent → explorer → file") interposed the
31881
+ * panel inside a conversation's family unit — the family clamp inside
31882
+ * focusedInsertionSlot still keeps any real family unsplit.
31883
+ */
31884
+ function workspaceExplorerSlot(panels, selectedIndex, facts) {
31885
+ return focusedInsertionSlot(panels, selectedIndex, facts.parentOf);
31937
31886
  }
31938
- //#endregion
31939
- //#region src/lib/components/conversation/panel/popups.svelte.ts
31940
- var Popups = class {
31941
- /** Chip popup state (2026-08-22, OCI pattern): ONE open chip at a time —
31942
- * the id of the expanded entry; detail renders AFTER the chip row.
31943
- * Per-panel by construction (moved here with the block). */
31944
- openChipId = null;
31945
- /** Peek popup state (ToolPeekButton port, 2026-08-22): the run key whose
31946
- * chip row has its list popup open — ONE at a time, and never while a
31947
- * chip detail popup is open (OCI mutual-exclusion contract). */
31948
- peekOpenRunKey = null;
31949
- thinkSuppressedId = null;
31950
- planSuppressedId = null;
31951
- /** The newest todo_write entry's id, STICKY: adopted only while
31952
- * streaming AND stuck (the detection window), then kept across newer
31953
- * thinks, turn end, and scroll-away — the plan stays on screen until
31954
- * a newer todo_write supersedes it or the user collapses it. */
31955
- autoPlanIdRaw = null;
31956
- deps;
31957
- stopPlanEffect;
31958
- constructor(deps) {
31959
- this.deps = deps;
31960
- this.stopPlanEffect = () => {};
31961
- }
31962
- destroy() {
31963
- this.stopPlanEffect();
31964
- }
31965
- /** The newest reasoning entry's id (wire order) while streaming AND
31966
- * stuck to the live end, else null — the transient think target. */
31967
- get autoThinkTargetId() {
31968
- if (!this.deps.store.isStreaming || !this.deps.stick) return null;
31969
- return newestReasoningId(this.deps.store.renderEntries);
31887
+ /**
31888
+ * The glass FIRST slot under its opener (operator ruling 2026-10-24):
31889
+ * a summoned/spawned glass lands DIRECTLY below its opener - past the
31890
+ * opener document tail, BEFORE every adopted child - matching the
31891
+ * spine tree wire order -1 (the ghost always reads as the first
31892
+ * child). Supersedes the region-end placement for the glass only.
31893
+ */
31894
+ function glassFirstSlot(panels, openedBy) {
31895
+ const openerIdx = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === openedBy);
31896
+ if (openerIdx === -1) return panels.length;
31897
+ return docTailEnd(panels, openerIdx, openedBy) + 1;
31898
+ }
31899
+ /**
31900
+ * Insert-before-focused slot (operator spec, 2026-08-31): a fresh panel
31901
+ * lands at the FOCUSED panel's index — immediately before it — and takes
31902
+ * focus (the route selects it). 0 when nothing is focused (empty floor,
31903
+ * stale selection) — the old front-add stays the fallback. Math.max keeps
31904
+ * splice's negative-index semantics out of the no-selection case.
31905
+ *
31906
+ * Family clamp (lineage I2): a slot strictly inside a family would wedge
31907
+ * the newcomer between a spawner and its pinned child — unitOf/blockBounds,
31908
+ * the sidebar's familyRenderOrder, and the adoption slot walk all assume
31909
+ * families stay contiguous on the floor. When the focus is a family CHILD,
31910
+ * the slot climbs to the family head — the nearest non-splitting slot left
31911
+ * of the focus (parentOf only carries on-floor parents, so an orphan child
31912
+ * is a root here, its own unit). A lone panel or a family head keeps the
31913
+ * exact focus slot.
31914
+ *
31915
+ * Manager focus (ADR D6): no session, no family — the slot is the focus
31916
+ * itself.
31917
+ *
31918
+ * Workspace member focus (2026-09-10): the explorer/file pair is a family
31919
+ * unit in the SAME block math (blockBounds — the Loadinjected D3 walk-back
31920
+ * grammar), so the clamp is the SAME one: insert at the unit head — the
31921
+ * PARENT position, left of the block — never between explorer and file.
31922
+ */
31923
+ function focusedInsertionSlot(panels, selectedIndex, parentOf) {
31924
+ const focus = Math.max(0, selectedIndex);
31925
+ const focused = panels[focus];
31926
+ if (focused === void 0) return 0;
31927
+ if (focused.kind === "workspace-explorer" || focused.kind === "workspace-file" || focused.kind === "browser") {
31928
+ const block = blockBounds(panels, focus, { parentOf });
31929
+ return block === null ? focus : block[0];
31970
31930
  }
31971
- /** Suppression-aware think target: null while the user kept THIS think
31972
- * collapsed (a newer think id simply stops matching). */
31973
- get autoThinkId() {
31974
- return suppressed(this.autoThinkTargetId, this.thinkSuppressedId);
31931
+ if (focused.kind !== "conversation") return focus;
31932
+ let head = focused.sessionId;
31933
+ for (;;) {
31934
+ const parent = parentOf.get(head) ?? null;
31935
+ if (parent === null) break;
31936
+ head = parent;
31975
31937
  }
31976
- /** Suppression-aware plan target (same id-tied rule as the think). */
31977
- get autoPlanId() {
31978
- return suppressed(this.autoPlanIdRaw, this.planSuppressedId);
31938
+ if (head === focused.sessionId) return focus;
31939
+ const headIndex = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === head);
31940
+ return headIndex >= 0 ? Math.min(headIndex, focus) : focus;
31941
+ }
31942
+ //#endregion
31943
+ //#region src/lib/services/panels/floor-registry/chrome.ts
31944
+ /**
31945
+ * floor-registry/chrome — panel chrome intents + explorer + file-intent
31946
+ * mailbox + removal (ADR-0022 W3).
31947
+ *
31948
+ * Moved WHOLE from the state home (floor-registry.svelte.ts): the
31949
+ * skill-shelf / plugin-rack chrome setters, the ONE-explorer-per-workspace
31950
+ * open grammar, the explorer tree/tab/git intents, the file-intent
31951
+ * publish/consume mailbox, and family-close removal. Plain module: every
31952
+ * write goes through the state home's reactive `st` hub and its
31953
+ * primitives (ADR-0019 D2: exactly ONE writer home for floor state).
31954
+ * Sibling rule: factories/slots are reached through the state home's
31955
+ * re-export hub, never by importing another sibling directly.
31956
+ */
31957
+ var d$4 = floorSeam;
31958
+ /** Chip click action: ONE explorer per WORKSPACE root across ALL
31959
+ * sessions — a click on any session sharing the root FOCUSES the open
31960
+ * explorer (dedupe key = root, D1); a fresh root lands at the focused
31961
+ * slot (D3, non-lineage — the explorer is not a lineage child).
31962
+ * sessionId rides the fresh panel as PROVENANCE (D4), never identity. */
31963
+ function openWorkspaceExplorer(sessionId, workspace) {
31964
+ const open = findWorkspaceExplorerPanel(st.panels, workspace);
31965
+ if (open) {
31966
+ st.selectedPanelId = open.id;
31967
+ return;
31979
31968
  }
31980
- toggleChip(id, autoOpen = false) {
31981
- if (autoOpen) {
31982
- if (id === this.autoPlanId) this.planSuppressedId = id;
31983
- else if (this.autoThinkId !== null && id === `r:${this.autoThinkId}`) this.thinkSuppressedId = this.autoThinkId;
31984
- return;
31985
- }
31986
- this.openChipId = this.openChipId === id ? null : id;
31987
- this.peekOpenRunKey = null;
31969
+ insertPanel(makeWorkspaceExplorerPanel(sessionId, workspace), workspaceExplorerSlot(st.panels, selectedIndex_(), d$4().getMoveFacts()));
31970
+ }
31971
+ /** Chip click action (The Glass Browser spec W2, ADR-0029 D4): ONE live
31972
+ * browser panel across the floor — the terminal singleton rule (Terminal
31973
+ * Desk ADR D2). A click on an open browser FOCUSES it; a miss inserts at
31974
+ * the focused slot. */
31975
+ function openBrowserPanel(index, keepSelection = false, width, openedBy, sessionId) {
31976
+ const open = st.panels.find((p) => p.kind === "browser" && p.openedBy === openedBy);
31977
+ if (open) {
31978
+ st.selectedPanelId = open.id;
31979
+ return;
31988
31980
  }
31989
- togglePeek(runKey) {
31990
- this.peekOpenRunKey = this.peekOpenRunKey === runKey ? null : runKey;
31991
- if (this.peekOpenRunKey !== null) this.openChipId = null;
31981
+ const prevSelected = st.selectedPanelId;
31982
+ const slot = index !== void 0 ? Math.max(0, Math.min(index, st.panels.length)) : openedBy !== void 0 ? glassFirstSlot(st.panels, openedBy) : insertionSlot();
31983
+ insertPanel(makeBrowserPanel(width, openedBy, sessionId), slot);
31984
+ if (keepSelection && prevSelected !== null && prevSelected !== void 0) {
31985
+ if (st.panels.some((p) => p.id === prevSelected)) st.selectedPanelId = prevSelected;
31992
31986
  }
31993
- };
31994
- //#endregion
31995
- //#region src/lib/components/conversation/panel/paging.svelte.ts
31987
+ }
31988
+ /** The open explorer for a session's workspace (The Shared Tree D1):
31989
+ * matched by ROOT across sessions — the panel's provenance sessionId
31990
+ * is not the key. */
31991
+ function sessionWorkspaceExplorer(sessionId) {
31992
+ const root = d$4().getWorkspaceFor(sessionId);
31993
+ return root === null ? void 0 : findWorkspaceExplorerPanel(st.panels, root);
31994
+ }
31995
+ /** Explorer tree intents: the panel emits toggle/open-file; the owner
31996
+ * mutates expanded (the persisted part) — the levels cache stays
31997
+ * component-local. */
31998
+ function toggleExplorerPath(panelId, path) {
31999
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32000
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32001
+ const explorer = st.panels[idx];
32002
+ const next = explorer.expanded.includes(path) ? explorer.expanded.filter((p) => p !== path) : [...explorer.expanded, path];
32003
+ st.panels = st.panels.with(idx, {
32004
+ ...explorer,
32005
+ expanded: next
32006
+ });
32007
+ }
32008
+ /** Explorer collapse-all intent: the panel emits; the owner empties the
32009
+ * expanded list (the persisted part). */
32010
+ function collapseExplorerPaths(panelId) {
32011
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32012
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32013
+ st.panels = st.panels.with(idx, {
32014
+ ...st.panels[idx],
32015
+ expanded: []
32016
+ });
32017
+ }
32018
+ /** Git Eye tab intent (2026-09-12): the active tab is PERSISTED on the
32019
+ * entry — a hard reload restores it. */
32020
+ function setExplorerTab(panelId, tab) {
32021
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32022
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32023
+ st.panels = st.panels.with(idx, {
32024
+ ...st.panels[idx],
32025
+ tab
32026
+ });
32027
+ }
32028
+ /** Explorer Layout tabs (amendment 2026-09-16): the tab list and the
32029
+ * active tab are OWNER-PERSISTED entry facts. The file tabs die with
32030
+ * the panel (D5). */
32031
+ function openExplorerTab(panelId, path) {
32032
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32033
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32034
+ const ex = st.panels[idx];
32035
+ const tabs = ex.openTabs ?? [];
32036
+ const openTabs = tabs.includes(path) ? tabs : [...tabs, path];
32037
+ st.panels = st.panels.with(idx, {
32038
+ ...ex,
32039
+ openTabs,
32040
+ activeFile: path
32041
+ });
32042
+ }
32043
+ function closeExplorerTab(panelId, path) {
32044
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32045
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32046
+ const ex = st.panels[idx];
32047
+ const tabs = ex.openTabs ?? [];
32048
+ const closedAt = tabs.indexOf(path);
32049
+ if (closedAt === -1) return;
32050
+ const openTabs = tabs.filter((p) => p !== path);
32051
+ const activeFile = ex.activeFile === path ? openTabs.length === 0 ? null : openTabs[Math.max(0, closedAt - 1)] : ex.activeFile ?? null;
32052
+ st.panels = st.panels.with(idx, {
32053
+ ...ex,
32054
+ openTabs,
32055
+ activeFile
32056
+ });
32057
+ }
32058
+ function activateExplorerTab(panelId, path) {
32059
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32060
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32061
+ const ex = st.panels[idx];
32062
+ if (!(ex.openTabs ?? []).includes(path)) return;
32063
+ st.panels = st.panels.with(idx, {
32064
+ ...ex,
32065
+ activeFile: path
32066
+ });
32067
+ }
32068
+ /** The Explorer Layout drag settle (ADR D4): one DESK fact — the split
32069
+ * is desk state, persisted with the floor blob, NOT per entry. The
32070
+ * panel already clamped; clamp again at the single bound site. */
32071
+ function setExplorerTreePct(_panelId, pct) {
32072
+ d$4().setTreePct(clampTreePct(pct));
32073
+ }
32074
+ /** Git Eye repo-collapse intent (2026-09-13): the collapsed repo set is
32075
+ * PERSISTED on the entry. Default is EXPANDED. */
32076
+ function toggleExplorerRepo(panelId, repoRel) {
32077
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32078
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32079
+ const explorer = st.panels[idx];
32080
+ const prev = explorer.collapsedRepos ?? [];
32081
+ const next = prev.includes(repoRel) ? prev.filter((r) => r !== repoRel) : [...prev, repoRel];
32082
+ st.panels = st.panels.with(idx, {
32083
+ ...explorer,
32084
+ collapsedRepos: next
32085
+ });
32086
+ }
32087
+ /** Git Eye collapse-all-repos intent (2026-09-13): collapses EVERY known
32088
+ * repo — persisted like the single-repo toggle. */
32089
+ function collapseExplorerRepos(panelId, repos) {
32090
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32091
+ if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
32092
+ const explorer = st.panels[idx];
32093
+ const prev = explorer.collapsedRepos ?? [];
32094
+ const next = [...prev, ...repos.filter((r) => !prev.includes(r))];
32095
+ st.panels = st.panels.with(idx, {
32096
+ ...explorer,
32097
+ collapsedRepos: next
32098
+ });
32099
+ }
32100
+ /** File Eye view intent (2026-09-13): diff vs edit is PERSISTED on the
32101
+ * entry. */
32102
+ function setFileView(panelId, view) {
32103
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32104
+ if (idx < 0 || st.panels[idx].kind !== "workspace-file") return;
32105
+ st.panels = st.panels.with(idx, {
32106
+ ...st.panels[idx],
32107
+ view
32108
+ });
32109
+ }
32110
+ /** Tree file-click action (The Settings Tree ADR 2026-09-18 D1): the
32111
+ * intent publishes to the session's explorer, which opens/focuses a
32112
+ * tab. No open explorer ⇒ the intent drops. */
32113
+ function openWorkspaceFile(sessionId, path, lines) {
32114
+ const host = sessionWorkspaceExplorer(sessionId);
32115
+ if (host) publishFileIntent(host.id, path, lines);
32116
+ }
32117
+ /** Publish a file intent to an explorer panel and focus it. `lines` is
32118
+ * the optional #L anchor target — display metadata, not persisted. */
32119
+ function publishFileIntent(explorerId, path, lines) {
32120
+ st.pendingFileIntents[explorerId] = {
32121
+ path,
32122
+ nonce: ++st.fileIntentSeq,
32123
+ lines: lines ?? null
32124
+ };
32125
+ st.selectedPanelId = explorerId;
32126
+ }
32127
+ /** The panel consumed intent nonce N — clear exactly that publish. */
32128
+ function consumeFileIntent(explorerId, nonce) {
32129
+ if (st.pendingFileIntents[explorerId]?.nonce === nonce) st.pendingFileIntents[explorerId] = null;
32130
+ }
32131
+ /** Remove a panel; family close walks the wire lineage transitively
32132
+ * (every descendant sub-agent goes with it, nested included). The
32133
+ * Shift+Click amendment (2026-09-01): family === false opts OUT — only
32134
+ * THAT panel drops, the children re-root as orphan family heads.
32135
+ *
32136
+ * The session's workspace pair closes with it (2026-09-10); closing an
32137
+ * EXPLORER dooms its file children BY PANEL ID — the stored
32138
+ * explorerPanelId edge, with the legacy null-edge fallback. A
32139
+ * settings-home explorer (sessionId null) dies with the close. */
32140
+ function removePanel(panelId, opts = {}) {
32141
+ const idx = st.panels.findIndex((p) => p.id === panelId);
32142
+ if (idx < 0) return;
32143
+ const root = st.panels[idx];
32144
+ const rootSessionId = root.kind === "conversation" ? root.sessionId : null;
32145
+ const doomed = new SvelteSet(rootSessionId !== null ? [rootSessionId] : []);
32146
+ if (opts.family !== false && rootSessionId !== null) {
32147
+ const stack = [rootSessionId];
32148
+ while (stack.length > 0) for (const kid of d$4().getLineageFacts().childrenByParent.get(stack.pop()) ?? []) if (!doomed.has(kid.sessionId)) {
32149
+ doomed.add(kid.sessionId);
32150
+ stack.push(kid.sessionId);
32151
+ }
32152
+ }
32153
+ const doomedPanelIds = new SvelteSet();
32154
+ if (opts.family !== false && rootSessionId !== null) {
32155
+ for (const p of st.panels) if (p.kind === "browser" && p.openedBy !== void 0 && doomed.has(p.openedBy)) doomedPanelIds.add(p.id);
32156
+ }
32157
+ if (opts.family !== false && root.kind === "workspace-explorer") {
32158
+ for (const p of st.panels) if (p.kind === "workspace-file" && (p.explorerPanelId === root.id || p.explorerPanelId === null && p.sessionId === root.sessionId)) doomedPanelIds.add(p.id);
32159
+ }
32160
+ st.panels = st.panels.filter((p) => p.id !== root.id && !doomedPanelIds.has(p.id) && (p.kind === "conversation" ? !doomed.has(p.sessionId) : !("sessionId" in p) || p.sessionId === null || opts.family !== false && !doomed.has(p.sessionId)));
32161
+ if (st.selectedPanelId !== null && !st.panels.some((p) => p.id === st.selectedPanelId)) st.selectedPanelId = st.panels[Math.min(idx, st.panels.length - 1)]?.id ?? null;
32162
+ clearLoupeIfGone();
32163
+ }
32164
+ //#endregion
32165
+ //#region src/lib/services/panels/floor-registry/drag.svelte.ts
32166
+ var d$2 = floorSeam;
32167
+ /** Honest-slider's "slider sets all panels" leg (REALTIME, 2026-08-27):
32168
+ * one width for every CONVERSATION column + the uniform preset.
32169
+ * 2026-09-13: the workspace kinds are FIXED lanes — the explorer and
32170
+ * file panels keep their own widths. */
32171
+ function resizeAll(width) {
32172
+ const w = clampPanelWidth(width);
32173
+ d$2().setPanelWidthPreset(w);
32174
+ st.panels = st.panels.map((p) => p.kind === "conversation" ? {
32175
+ ...p,
32176
+ width: w
32177
+ } : p);
32178
+ }
32179
+ /** A transcript file link publishes ONLY after the tree route proves
32180
+ * the file exists inside the session's workspace — the gate lives in
32181
+ * the shared util; anything else drops the click. No navigation ever
32182
+ * happens. No explorer yet ⇒ OPEN ONE (amendment 2026-09-25): the link
32183
+ * itself opens the panel; without a workspace root there is nothing to
32184
+ * open. W2 (T2.3): moved wholesale from the route — pure machine over
32185
+ * the deps seam + the shared utils. */
32186
+ async function openFileLinkFromMarkdown(sessionId, rawHref) {
32187
+ const { path, lines } = parseFileLinkHref(rawHref);
32188
+ if (path === "") return;
32189
+ const target = await resolveFileLinkTarget(sessionId, path, d$2().getWorkspaceFor(sessionId), void 0, workspaceRootDirs);
32190
+ if (target === null) return;
32191
+ if (!sessionWorkspaceExplorer(sessionId)) {
32192
+ const root = d$2().getWorkspaceFor(sessionId);
32193
+ if (!root) return;
32194
+ openWorkspaceExplorer(sessionId, root);
32195
+ }
32196
+ openWorkspaceFile(sessionId, target, lines);
32197
+ }
32198
+ /** /events?full=1 carries entries+lastSeq+running+pending/settled
32199
+ * answers (the ledger-tail resync surface); /history carries hasMore.
32200
+ * Its beforeSeq anchor needs the tail's lastSeq, so the events response
32201
+ * resolves first — the only serialization. W2 (T2.6): moved wholesale
32202
+ * from the route — a pure wire read over the global fetch. */
32203
+ async function fetchColdFromWire(sessionId) {
32204
+ const eventsRes = await fetch(`/api/dsh/session/${encodeURIComponent(sessionId)}/events?full=1`);
32205
+ if (!eventsRes.ok) return null;
32206
+ const events = await eventsRes.json();
32207
+ if (!events.ok) return null;
32208
+ const lastSeq = events.lastSeq ?? -1;
32209
+ let hasMore = false;
32210
+ const historyRes = await fetch(`/api/dsh/session/${encodeURIComponent(sessionId)}/history?beforeSeq=${lastSeq + 1}`);
32211
+ if (historyRes.ok) {
32212
+ const history = await historyRes.json();
32213
+ if (history.ok) hasMore = (history.entries?.length ?? 0) > 0 || history.hasMore === true;
32214
+ }
32215
+ return {
32216
+ title: null,
32217
+ workspace: null,
32218
+ agentPreset: null,
32219
+ entries: events.entries ?? [],
32220
+ lastSeq,
32221
+ running: events.running ?? false,
32222
+ hasMore,
32223
+ pendingAnswers: events.pendingAnswers ?? [],
32224
+ settledAnswers: events.settledAnswers ?? [],
32225
+ permission: events.permission ?? null,
32226
+ imageLimits: events.imageLimits ?? null,
32227
+ knobEvents: events.knobEvents ?? [],
32228
+ deliverables: events.deliverables ?? []
32229
+ };
32230
+ }
32231
+ //#endregion
32232
+ //#region src/lib/services/panels/floor-registry/factories.ts
32233
+ /**
32234
+ * floor-registry/factories — panel factories + placement slots (ADR-0022 W2).
32235
+ *
32236
+ * Moved WHOLE from the state home (floor-registry.svelte.ts): the eight
32237
+ * make* panel factories and the slot grammar (insertPanel, insertionSlot,
32238
+ * pinnedSlotIndex). Plain module: no runes here — every write goes through
32239
+ * the state home's reactive `st` hub and its primitives (ADR-0019 D2:
32240
+ * exactly ONE writer home for floor state). Sibling rule: this file never
32241
+ * imports another sibling; shared needs come from the state home.
32242
+ */
32243
+ var d$1 = floorSeam;
32244
+ function makePanel(sessionId, agentPreset) {
32245
+ return {
32246
+ id: nextPanelId(),
32247
+ kind: "conversation",
32248
+ sessionId,
32249
+ agentPreset,
32250
+ width: clampPanelWidth(d$1().getPanelWidth())
32251
+ };
32252
+ }
32253
+ /** width: the reload-restored panel re-mints at the width the operator
32254
+ * dragged before (browser-slot persistence, RCA 2026-10-22); absent for
32255
+ * chip clicks and legacy slots — the honest-slider preset.
32256
+ * openedBy: the opener session's provenance, recorded ONCE at spawn and
32257
+ * never rewritten (ADR-0036 D1/D2); undefined = no conversation context. */
32258
+ function makeBrowserPanel(width, openedBy, sessionId) {
32259
+ return {
32260
+ id: nextPanelId(),
32261
+ kind: "browser",
32262
+ width: clampPanelWidth(width ?? browserPanelWidth(), "browser"),
32263
+ ...openedBy !== void 0 ? { openedBy } : {},
32264
+ ...sessionId !== void 0 ? { browserSessionId: sessionId } : {}
32265
+ };
32266
+ }
32267
+ function makeWorkspaceExplorerPanel(sessionId, root) {
32268
+ return {
32269
+ id: nextPanelId(),
32270
+ kind: "workspace-explorer",
32271
+ sessionId,
32272
+ root,
32273
+ expanded: [],
32274
+ width: clampPanelWidth(850, "workspace-explorer")
32275
+ };
32276
+ }
32277
+ function insertPanel(entry, index) {
32278
+ const next = [...st.panels];
32279
+ next.splice(Math.min(index, next.length), 0, entry);
32280
+ st.panels = next;
32281
+ st.selectedPanelId = entry.id;
32282
+ }
32283
+ function insertionSlot() {
32284
+ return focusedInsertionSlot(st.panels, selectedIndex_(), d$1().getMoveFacts().parentOf);
32285
+ }
32286
+ //#endregion
32287
+ //#region src/lib/services/panels/floor-registry.svelte.ts
32288
+ var deps = null;
32289
+ function configureFloorRegistry(d0) {
32290
+ deps = d0;
32291
+ }
32292
+ function d() {
32293
+ if (deps === null) throw new Error("floor-registry: configureFloorRegistry() was never called");
32294
+ return deps;
32295
+ }
32296
+ /** Sibling-module access to the dep seam (ADR-0022 W2): the ONE outside-reads
32297
+ * channel; siblings never open their own config channel. */
32298
+ function floorSeam() {
32299
+ return d();
32300
+ }
32301
+ var st = {
32302
+ panels: [],
32303
+ selectedPanelId: null,
32304
+ loupePanelId: null,
32305
+ focusComposerId: null,
32306
+ panelIdSeq: 0,
32307
+ terminalDeskMirrors: {},
32308
+ pendingFileIntents: {},
32309
+ fileIntentSeq: 0,
32310
+ drag: null,
32311
+ widthBadgeFading: false
32312
+ };
32313
+ /** Non-reactive gesture refs (drag start index, fade timer, badge ids) —
32314
+ * written by floor-registry/drag.svelte.ts; the ids set is a live
32315
+ * SvelteSet so badge visibility stays reactive. */
32316
+ var dragRefs = {
32317
+ ids: new SvelteSet()
32318
+ };
32319
+ var floor = {
32320
+ get panels() {
32321
+ return st.panels;
32322
+ },
32323
+ get selectedPanelId() {
32324
+ return st.selectedPanelId;
32325
+ },
32326
+ get loupePanelId() {
32327
+ return st.loupePanelId;
32328
+ },
32329
+ get focusComposerId() {
32330
+ return st.focusComposerId;
32331
+ },
32332
+ get terminalDeskMirrors() {
32333
+ return st.terminalDeskMirrors;
32334
+ },
32335
+ get pendingFileIntents() {
32336
+ return st.pendingFileIntents;
32337
+ },
32338
+ get drag() {
32339
+ return st.drag;
32340
+ },
32341
+ get widthBadgeFading() {
32342
+ return st.widthBadgeFading;
32343
+ },
32344
+ get widthBadgeIds() {
32345
+ return dragRefs.ids;
32346
+ },
32347
+ get selected() {
32348
+ return st.panels.find((p) => p.id === st.selectedPanelId) ?? null;
32349
+ },
32350
+ get loupePanel() {
32351
+ return st.panels.find((p) => p.id === st.loupePanelId) ?? null;
32352
+ }
32353
+ };
32354
+ function nextPanelId() {
32355
+ st.panelIdSeq += 1;
32356
+ return "panel-" + st.panelIdSeq;
32357
+ }
32358
+ function syncPanelIdSeq(entries) {
32359
+ for (const p of entries) {
32360
+ const m = /^panel-(\d+)$/.exec(p.id);
32361
+ if (m) st.panelIdSeq = Math.max(st.panelIdSeq, Number(m[1]));
32362
+ }
32363
+ }
32364
+ function initFloor(entries, selectedId) {
32365
+ st.panels = entries;
32366
+ st.selectedPanelId = selectedId;
32367
+ syncPanelIdSeq(entries);
32368
+ }
32369
+ /** The fresh panel mounted and its composer took focus — clear the
32370
+ * swap-focus marker (a marker whose panel never mounts stays inert:
32371
+ * panel ids are unique and never remount). */
32372
+ function clearFocusComposer() {
32373
+ st.focusComposerId = null;
32374
+ }
32375
+ function selectPanel(panelId) {
32376
+ if (st.panels.some((p) => p.id === panelId)) st.selectedPanelId = panelId;
32377
+ }
32378
+ function closeLoupe() {
32379
+ st.loupePanelId = null;
32380
+ }
32381
+ function clearLoupeIfGone() {
32382
+ if (st.loupePanelId !== null && !st.panels.some((p) => p.id === st.loupePanelId)) st.loupePanelId = null;
32383
+ }
32384
+ function selectedIndex_() {
32385
+ return st.panels.findIndex((p) => p.id === st.selectedPanelId);
32386
+ }
32387
+ //#endregion
32388
+ //#region src/lib/utils/compact-number.ts
32389
+ /** Compact number formatting for narrow UI badges: 1829 -> '1.8K',
32390
+ * 4200000 -> '4.2M'. Locale-stable ('en') so rack rows keep their width. */
32391
+ var compact = new Intl.NumberFormat("en", {
32392
+ notation: "compact",
32393
+ maximumFractionDigits: 1
32394
+ });
32395
+ function formatCompact(n) {
32396
+ return compact.format(n);
32397
+ }
32398
+ //#endregion
32399
+ //#region src/lib/components/anchor/subagent-anchor/SubagentPopup.svelte
32400
+ function SubagentPopup($$renderer, $$props) {
32401
+ $$renderer.component(($$renderer) => {
32402
+ /**
32403
+ * SubagentPopup — the ledger half of the Subagent Anchor (Subagent
32404
+ * Anchor ADR D1/D5, amendment D6/D7): every spawned session as ONE flat
32405
+ * row — title · tokens · elapsed — inside the house popup shell (Esc /
32406
+ * outside close, right-full placement, width cap). The popup owns NO
32407
+ * state: rows arrive as props from the anchor button's poll; elapsed
32408
+ * lives only in the subagent-clock registry; lineage is re-derived
32409
+ * per fetch upstream, never written here.
32410
+ *
32411
+ * D6 header: shell title row + the count chip (N helpers, M running).
32412
+ * D5 sort: running first, then token spend descending (nulls last).
32413
+ * D7 footer: Σ over EXACTLY the header's rows; any null-degraded row
32414
+ * flips it to the honest Σ-incomplete marker — a partial sum must never
32415
+ * read as whole. Elapsed is NOT summed (concurrent wall-clocks add to
32416
+ * nothing). The footnote states the one-tick first-sight bias.
32417
+ */
32418
+ let { open = false, triggerEl = void 0, rows, onopenrow } = $$props;
32419
+ const spend = (row) => row.tokens === null ? -1 : row.tokens.input + row.tokens.output;
32420
+ /** D5: running first, then token spend desc (unknown spends last),
32421
+ * then session id for a stable tiebreak. */
32422
+ const sorted = derived(() => [...rows].sort((a, b) => Number(b.running) - Number(a.running) || spend(b) - spend(a) || a.sessionId.localeCompare(b.sessionId)));
32423
+ const runningCount = derived(() => rows.filter((r) => r.running).length);
32424
+ const anyUnknown = derived(() => rows.some((r) => r.tokens === null));
32425
+ const totalTokens = derived(() => rows.reduce((acc, r) => r.tokens === null ? acc : acc + r.tokens.input + r.tokens.output, 0));
32426
+ /** True when this helper's panel is already open on the floor — the
32427
+ * row tints so the operator sees which helpers live in the zoom. */
32428
+ function onFloor(sessionId) {
32429
+ return floorPanelId(sessionId) !== null;
32430
+ }
32431
+ /** The floor panel id hosting this helper, null when not open. */
32432
+ function floorPanelId(sessionId) {
32433
+ return floor.panels.find((panel) => "sessionId" in panel && panel.sessionId === sessionId)?.id ?? null;
32434
+ }
32435
+ /** Any helper live on the floor — the close-all X's visibility. */
32436
+ const anyOnFloor = derived(() => rows.some((r) => onFloor(r.sessionId)));
32437
+ /** Compact ledger form, DSH GUI parity — null renders the dash (never 0). */
32438
+ function tokensLabel(row) {
32439
+ if (row.tokens === null) return "—";
32440
+ return `${formatCompact(row.tokens.input + row.tokens.output)} tok`;
32441
+ }
32442
+ /** Elapsed label — the ledger's operate time (llmMs, the stats bar's
32443
+ * own 'LLM …' number, settled rows included). The first-sight clock is
32444
+ * the FALLBACK for rows the host cannot date; null renders the dash
32445
+ * (never a guess). */
32446
+ function elapsedLabel(row) {
32447
+ let ms;
32448
+ if (typeof row.operateMs === "number") ms = row.operateMs;
32449
+ else ms = subagentClock.elapsedMs(row.sessionId);
32450
+ if (ms === null) return "—";
32451
+ const s = Math.floor(ms / 1e3);
32452
+ if (s < 60) return `${s}s`;
32453
+ const m = Math.floor(s / 60);
32454
+ if (m < 60) return `${m}m ${s % 60}s`;
32455
+ return `${Math.floor(m / 60)}h ${m % 60}m`;
32456
+ }
32457
+ let $$settled = true;
32458
+ let $$inner_renderer;
32459
+ function $$render_inner($$renderer) {
32460
+ FloatingAnchorContainerPopup($$renderer, {
32461
+ triggerEl,
32462
+ popupTestId: "subagent-popup",
32463
+ get open() {
32464
+ return open;
32465
+ },
32466
+ set open($$value) {
32467
+ open = $$value;
32468
+ $$settled = false;
32469
+ },
32470
+ children: ($$renderer) => {
32471
+ $$renderer.push(`<div class="flex items-center border-b border-surface-border px-2.5 py-1 text-[10px] uppercase tracking-wider text-text-muted" data-testid="subagent-popup-chip"><span class="flex-1">${escape_html(rows.length)}
32472
+ ${escape_html(rows.length === 1 ? "subagent" : "subagents")} · ${escape_html(runningCount())} running</span> `);
32473
+ if (anyOnFloor()) {
32474
+ $$renderer.push("<!--[0-->");
32475
+ $$renderer.push(`<button type="button" class="shrink-0 transition-colors"${attr("title", t(subagentcloseallpanels3))}${attr("aria-label", t(subagentcloseallpanels3))} data-testid="subagent-popup-close-all">`);
32476
+ Trash_2($$renderer, {
32477
+ size: 14,
32478
+ "aria-hidden": "true",
32479
+ style: "stroke: red;"
32480
+ });
32481
+ $$renderer.push(`<!----></button>`);
32482
+ } else $$renderer.push("<!--[-1-->");
32483
+ $$renderer.push(`<!--]--></div> <!--[-->`);
32484
+ const each_array = ensure_array_like(sorted());
32485
+ for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
32486
+ let row = each_array[$$index];
32487
+ $$renderer.push(`<button type="button"${attr_class(`flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs transition-colors hover:bg-surface-hover ${onFloor(row.sessionId) ? "row-on-floor" : ""}`, "svelte-1lupp7r")} data-testid="subagent-row"><span${attr_class(`flex shrink-0 items-center ${row.running ? "text-amber-500" : "text-text-muted"}`)} data-testid="subagent-row-glyph">`);
32488
+ if (row.running) {
32489
+ $$renderer.push("<!--[0-->");
32490
+ Loader_circle($$renderer, {
32491
+ size: 11,
32492
+ class: "animate-spin"
32493
+ });
32494
+ } else {
32495
+ $$renderer.push("<!--[-1-->");
32496
+ Circle_dashed($$renderer, { size: 11 });
32497
+ }
32498
+ $$renderer.push(`<!--]--></span> <span class="min-w-0 flex-1 truncate">${escape_html(row.title ?? "—")}</span> <span class="w-20 shrink-0 text-right font-variant-numeric tabular-nums" data-testid="subagent-row-tokens">${escape_html(tokensLabel(row))}</span> <span class="w-14 shrink-0 text-right text-text-muted tabular-nums" data-testid="subagent-row-elapsed">${escape_html(elapsedLabel(row))}</span></button>`);
32499
+ }
32500
+ $$renderer.push(`<!--]--> <div${attr_class(`sticky bottom-0 flex items-center border-t border-surface-border bg-surface-elevated px-2.5 py-1.5 text-[12px] font-medium tracking-wider ${anyUnknown() ? "text-text-muted" : "text-text-primary"}`)} data-testid="subagent-popup-footer" style="padding-right: 6em;"><span class="flex-1 text-right">${escape_html(t(() => totaltokens1()))}</span> <span class="w-20 shrink-0 text-right font-variant-numeric tabular-nums" data-testid="subagent-popup-footer-tokens">${escape_html(anyUnknown() ? "—" : `${formatCompact(totalTokens())} tok`)}</span></div>`);
32501
+ },
32502
+ $$slots: { default: true }
32503
+ });
32504
+ }
32505
+ do {
32506
+ $$settled = true;
32507
+ $$inner_renderer = $$renderer.copy();
32508
+ $$render_inner($$inner_renderer);
32509
+ } while (!$$settled);
32510
+ $$renderer.subsume($$inner_renderer);
32511
+ bind_props($$props, { open });
32512
+ });
32513
+ }
32514
+ //#endregion
32515
+ //#region src/lib/components/anchor/subagent-anchor/SubagentAnchorButton.svelte
32516
+ function SubagentAnchorButton($$renderer, $$props) {
32517
+ $$renderer.component(($$renderer) => {
32518
+ /**
32519
+ * SubagentAnchorButton — the ledger leaf of the conversation page's
32520
+ * FloatingAnchor stack (Subagent Anchor ADR D1, placement REVISED
32521
+ * 2026-10-26: the operator's 'anchor' means the right-edge icon
32522
+ * column beside the transcript — JobsButton's seat — NOT the sidebar
32523
+ * header). Lists THIS conversation's sub-agents only (the ?parent
32524
+ * scoping, DSH dropdown parity) — one click: title, tokens, elapsed.
32525
+ * Hidden at zero rows so the stack keeps its shape. Owns the poll:
32526
+ * one GET per few seconds while mounted, each answer fed to the
32527
+ * subagent-clock registry as its spine tick (ADR D3).
32528
+ */
32529
+ let { onopensession = void 0} = $$props;
32530
+ let rows = [];
32531
+ let open = false;
32532
+ /** The toggle's wrapper — the shell's close-contract exclusion. */
32533
+ let triggerEl = void 0;
32534
+ const runningCount = derived(() => rows.filter((r) => r.running).length);
32535
+ let $$settled = true;
32536
+ let $$inner_renderer;
32537
+ function $$render_inner($$renderer) {
32538
+ if (rows.length > 0) {
32539
+ $$renderer.push("<!--[0-->");
32540
+ $$renderer.push(`<div data-svelte="SubagentAnchorButton" class="relative" data-testid="subagent-anchor-button"><button type="button"${attr_class(`floating-anchor-btn ${runningCount() > 0 ? "tone-purple" : "tone-idle"}`, "svelte-1vxt4fg", { "active": open })}${attr("title", t(subagentanchorlabel2))}${attr("aria-label", t(subagentanchorlabel2))} data-testid="subagent-anchor-trigger">`);
32541
+ Bot($$renderer, {
32542
+ size: 13,
32543
+ "aria-hidden": "true"
32544
+ });
32545
+ $$renderer.push(`<!----> <span data-testid="subagent-anchor-badge">${escape_html(runningCount() > 0 ? runningCount() : rows.length)}</span></button> `);
32546
+ SubagentPopup($$renderer, {
32547
+ triggerEl,
32548
+ rows,
32549
+ onopenrow: onopensession,
32550
+ get open() {
32551
+ return open;
32552
+ },
32553
+ set open($$value) {
32554
+ open = $$value;
32555
+ $$settled = false;
32556
+ }
32557
+ });
32558
+ $$renderer.push(`<!----></div>`);
32559
+ } else $$renderer.push("<!--[-1-->");
32560
+ $$renderer.push(`<!--]-->`);
32561
+ }
32562
+ do {
32563
+ $$settled = true;
32564
+ $$inner_renderer = $$renderer.copy();
32565
+ $$render_inner($$inner_renderer);
32566
+ } while (!$$settled);
32567
+ $$renderer.subsume($$inner_renderer);
32568
+ });
32569
+ }
32570
+ //#endregion
32571
+ //#region src/lib/components/conversation/panel/ConversationFloatingAnchor.svelte
32572
+ function ConversationFloatingAnchor($$renderer, $$props) {
32573
+ /**
32574
+ * ConversationFloatingAnchor — the conversation page's floating action
32575
+ * stack (OCI Floating Host port, 2026-08-23; extracted from the page
32576
+ * 2026-08-24): jump and scroll actions beside the transcript they act
32577
+ * on — the User Messages button (self-contained with its popup jumper
32578
+ * since 2026-08-26; renders only when prompt groups exist) and the
32579
+ * far-edge jump. Purely presentational — the viewport and turn groups
32580
+ * arrive as props.
32581
+ */
32582
+ let { groups, container, todos = null, deliverables = null, onfileopen = void 0, jobsessionid = "", shelfentries = void 0, shelfsessionid = "", shelfworkspace = null, shelfpanelid = null, onshelfnote } = $$props;
32583
+ if (container !== void 0) {
32584
+ $$renderer.push("<!--[0-->");
32585
+ FloatingAnchor($$renderer, {
32586
+ children: ($$renderer) => {
32587
+ PlanButton($$renderer, { todos: todos ?? [] });
32588
+ $$renderer.push(`<!----> `);
32589
+ DeliverablesButton($$renderer, {
32590
+ deliveries: deliverables,
32591
+ onfileopen
32592
+ });
32593
+ $$renderer.push(`<!----> `);
32594
+ JobsButton($$renderer, { sessionId: jobsessionid });
32595
+ $$renderer.push(`<!----> `);
32596
+ SubagentAnchorButton($$renderer, { });
32597
+ $$renderer.push(`<!----> `);
32598
+ if (shelfentries !== void 0) {
32599
+ $$renderer.push("<!--[0-->");
32600
+ InjectedShelfButton($$renderer, {
32601
+ entries: shelfentries,
32602
+ sessionId: shelfsessionid,
32603
+ workspace: shelfworkspace,
32604
+ panelId: shelfpanelid,
32605
+ onnote: onshelfnote
32606
+ });
32607
+ } else $$renderer.push("<!--[-1-->");
32608
+ $$renderer.push(`<!--]--> `);
32609
+ UserMessagesButton($$renderer, {
32610
+ groups,
32611
+ container
32612
+ });
32613
+ $$renderer.push(`<!----> `);
32614
+ BackToTheEdgeButton($$renderer);
32615
+ $$renderer.push(`<!---->`);
32616
+ }});
32617
+ } else $$renderer.push("<!--[-1-->");
32618
+ $$renderer.push(`<!--]-->`);
32619
+ }
32620
+ //#endregion
32621
+ //#region src/lib/components/conversation/panel/A2aChipStack.svelte
32622
+ function A2aChipStack($$renderer, $$props) {
32623
+ $$renderer.component(($$renderer) => {
32624
+ /**
32625
+ * A2aChipStack — the sender panel's read-only delegation-ledger join
32626
+ * (W4 4.2): one floating chip per recent exchange THIS panel sent,
32627
+ * rendered beside the transcript on the floating-anchor pattern
32628
+ * (right-middle stack, per-panel via the embedded anchor host).
32629
+ *
32630
+ * Honesty contract (PRD risk "Attribution overclaim in UI" + spec-check
32631
+ * GAP-5): chip text derives from the row's `state` VERBATIM, all six
32632
+ * states distinguished — `replied_approx` renders "may have replied",
32633
+ * NEVER the confident "replied" string. The ledger is the truth; this
32634
+ * component claims nothing the row does not say.
32635
+ *
32636
+ * Read-only by construction (BC-2): rows arrive as a prop from the
32637
+ * page's EXISTING spine-cadence poll of GET /api/a2a?from= — the chip
32638
+ * owns NO fetch, NO timer. Click = the existing panel doorway
32639
+ * (addPanelFromSidebar dedupe→select). Dismiss is LOCAL-ONLY visual:
32640
+ * rows are durable; the next poll brings a dismissed chip back only
32641
+ * when it is still in the page's fetched window.
32642
+ */
32643
+ let { rows, titles } = $$props;
32644
+ /** Local-only visual dismissal — rows are durable; the ledger is truth. */
32645
+ let dismissed = /* @__PURE__ */ new Set();
32646
+ /** The cap: newest 3 visible + one overflow indicator (spec 4.2). */
32647
+ const CHIP_CAP = 3;
32648
+ const visible = derived(() => rows.filter((r) => !dismissed.has(r.id)));
32649
+ const capped = derived(() => visible().slice(0, CHIP_CAP));
32650
+ const overflow = derived(() => Math.max(visible().length - CHIP_CAP, 0));
32651
+ /**
32652
+ * Chip text derives from the state VERBATIM (spec-check GAP-5): every
32653
+ * state distinguished, `replied_approx` hedged as "may have replied" —
32654
+ * never a confident claim the row cannot support.
32655
+ */
32656
+ function labelFor(state) {
32657
+ switch (state) {
32658
+ case "waiting": return "waiting";
32659
+ case "replied_exact": return "replied (exact)";
32660
+ case "replied": return "replied";
32661
+ case "replied_approx": return "may have replied";
32662
+ case "timeout": return "no reply (timeout)";
32663
+ case "gone": return "target gone";
32664
+ }
32665
+ }
32666
+ /** The target's display title — the spine title, else the id prefix. */
32667
+ function titleFor(toSession) {
32668
+ return titles[toSession] ?? toSession.replace(/^session-/, "").slice(0, 8);
32669
+ }
32670
+ if (visible().length > 0) {
32671
+ $$renderer.push("<!--[0-->");
32672
+ FloatingAnchor($$renderer, {
32673
+ children: ($$renderer) => {
32674
+ $$renderer.push(`<!--[-->`);
32675
+ const each_array = ensure_array_like(capped());
32676
+ for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
32677
+ let row = each_array[$$index];
32678
+ $$renderer.push(`<div class="a2a-chip-wrap" data-testid="a2a-chip"${attr("data-a2a-state", row.state)}${attr("data-a2a-id", row.id)}><button type="button"${attr_class("a2a-chip", void 0, {
32679
+ "tone-waiting": row.state === "waiting",
32680
+ "tone-replied-exact": row.state === "replied_exact",
32681
+ "tone-replied": row.state === "replied",
32682
+ "tone-replied-approx": row.state === "replied_approx",
32683
+ "tone-timeout": row.state === "timeout",
32684
+ "tone-gone": row.state === "gone"
32685
+ })} data-testid="a2a-chip-button"${attr("title", `${stringify(titleFor(row.toSession))} — ${stringify(labelFor(row.state))}`)}>`);
32686
+ if (row.state === "waiting") {
32687
+ $$renderer.push("<!--[0-->");
32688
+ Hourglass($$renderer, { size: 13 });
32689
+ } else if (row.state === "replied_exact" || row.state === "replied") {
32690
+ $$renderer.push("<!--[1-->");
32691
+ Check($$renderer, { size: 13 });
32692
+ } else $$renderer.push("<!--[-1-->");
32693
+ $$renderer.push(`<!--]--> <span class="a2a-chip-label">${escape_html(labelFor(row.state))}</span></button> <button type="button" class="a2a-dismiss" data-testid="a2a-chip-dismiss" title="hide this chip (the ledger row stays)">`);
32694
+ X($$renderer, { size: 11 });
32695
+ $$renderer.push(`<!----></button></div>`);
32696
+ }
32697
+ $$renderer.push(`<!--]--> `);
32698
+ if (overflow() > 0) {
32699
+ $$renderer.push("<!--[0-->");
32700
+ $$renderer.push(`<div class="a2a-chip-overflow" data-testid="a2a-chip-overflow">+${escape_html(overflow())} more</div>`);
32701
+ } else $$renderer.push("<!--[-1-->");
32702
+ $$renderer.push(`<!--]-->`);
32703
+ }});
32704
+ } else $$renderer.push("<!--[-1-->");
32705
+ $$renderer.push(`<!--]-->`);
32706
+ });
32707
+ }
32708
+ //#endregion
32709
+ //#region src/lib/components/conversation/panel/ConversationStatsBar.svelte
32710
+ function ConversationStatsBar($$renderer, $$props) {
32711
+ $$renderer.component(($$renderer) => {
32712
+ /**
32713
+ * ConversationStatsBar — the panel's session baseline (ADR 2026-09-08
32714
+ * "The Stats Bar", D1/D3/D5): one fixed-height line under the transcript
32715
+ * showing what the ledger already proves — turns, steps, tool time,
32716
+ * token totals, cache hit. Pure presentation: the panel derives the
32717
+ * stats and hands them down; segments whose inputs never reached DSI
32718
+ * are omitted, never approximated. Renders nothing before the first
32719
+ * assistant message (D5 gate lives in the derive's null return).
32720
+ */
32721
+ let { stats } = $$props;
32722
+ /** 85000 -> "1m25s"; 2400000 -> "40m"; 5400000 -> "1h30m"; 250 -> "250ms". */
32723
+ function formatDuration(ms) {
32724
+ if (ms < 1e3) return `${ms}ms`;
32725
+ const totalSec = Math.floor(ms / 1e3);
32726
+ const h = Math.floor(totalSec / 3600);
32727
+ const m = Math.floor(totalSec % 3600 / 60);
32728
+ const s = totalSec % 60;
32729
+ if (h > 0) return `${h}h${m}m`;
32730
+ if (m > 0) return s > 0 ? `${m}m${s}s` : `${m}m`;
32731
+ return `${s}s`;
32732
+ }
32733
+ /** 15800 -> "15.8K"; 56300 -> "56.3K"; 15800000 -> "15.8M"; 108000 -> "108K"; 999 -> "999". */
32734
+ function formatTokens(n) {
32735
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
32736
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}K`;
32737
+ return String(n);
32738
+ }
32739
+ const segments = derived(() => {
32740
+ if (stats === null) return [];
32741
+ const parts = [`${stats.turns} turns · ${stats.steps} steps`];
32742
+ const durations = [];
32743
+ if (stats.llmMs !== void 0 && stats.llmMs > 0) durations.push(`LLM ${formatDuration(stats.llmMs)}`);
32744
+ if (stats.toolMs > 0) durations.push(`Tool ${formatDuration(stats.toolMs)}`);
32745
+ if (durations.length > 0) parts.push(durations.join(" · "));
32746
+ const speeds = [];
32747
+ if (stats.ttftSteps !== void 0 && stats.ttftSteps > 0 && stats.ttftMs !== void 0) speeds.push(`TTFT avg ${formatDuration(stats.ttftMs / stats.ttftSteps)}`);
32748
+ if (stats.decodeMs !== void 0 && stats.decodeMs > 0 && stats.decodeTokens !== void 0) speeds.push(`${Math.max(1, Math.round(stats.decodeTokens / (stats.decodeMs / 1e3)))} tok/s`);
32749
+ if (speeds.length > 0) parts.push(speeds.join(" · "));
32750
+ const inputTotal = stats.billedInputTokens ?? stats.inputTokens;
32751
+ if (inputTotal !== void 0) parts.push(`Input ${formatTokens(inputTotal)} tok · Output ${formatTokens(stats.outputTokens ?? 0)} tok`);
32752
+ if (stats.cacheHitPercent !== void 0) parts.push(`Cache hit ${stats.cacheHitPercent}%`);
32753
+ return parts;
32754
+ });
32755
+ if (stats !== null) {
32756
+ $$renderer.push("<!--[0-->");
32757
+ $$renderer.push(`<div data-svelte="ConversationStatsBar" class="stats-bar svelte-hnxn10" data-testid="conversation-stats-bar"${attr("title", segments().join(" | "))}><!--[-->`);
32758
+ const each_array = ensure_array_like(segments());
32759
+ for (let i = 0, $$length = each_array.length; i < $$length; i++) {
32760
+ let segment = each_array[i];
32761
+ if (i > 0) {
32762
+ $$renderer.push("<!--[0-->");
32763
+ $$renderer.push(`<span class="sep svelte-hnxn10" aria-hidden="true">|</span>`);
32764
+ } else $$renderer.push("<!--[-1-->");
32765
+ $$renderer.push(`<!--]--> <span class="segment svelte-hnxn10" data-testid="stats-segment">${escape_html(segment)}</span>`);
32766
+ }
32767
+ $$renderer.push(`<!--]--></div>`);
32768
+ } else $$renderer.push("<!--[-1-->");
32769
+ $$renderer.push(`<!--]-->`);
32770
+ });
32771
+ }
32772
+ //#endregion
32773
+ //#region src/lib/components/conversation/panel/submit-ladder.svelte.ts
32774
+ var SubmitLadder = class {
32775
+ deps;
32776
+ /** True while the plain-send rung is in flight — the footer's sending flag. */
32777
+ submitted = false;
32778
+ /** Inline command feedback (role=status, auto-dismiss 5s) — the ONLY
32779
+ * command UI surface; the input has already cleared by intercept time.
32780
+ * The wire work lives in command-executor (Prompt Macro W1 1.2): the
32781
+ * /new create+swap, /permission route, and mention delivery are the
32782
+ * SAME bytes the typed path always sent — the panel now renders
32783
+ * outcomes only (banner kind from result.ok). */
32784
+ commandNote = null;
32785
+ commandNoteTimer;
32786
+ constructor(deps) {
32787
+ this.deps = deps;
32788
+ }
32789
+ /** Inline command feedback — every rung banners through here. */
32790
+ noteCommand(kind, text) {
32791
+ this.commandNote = {
32792
+ kind,
32793
+ text
32794
+ };
32795
+ if (this.commandNoteTimer !== void 0) clearTimeout(this.commandNoteTimer);
32796
+ this.commandNoteTimer = setTimeout(() => this.commandNote = null, 5e3);
32797
+ }
32798
+ async onsubmit(text, images) {
32799
+ if (isTypedRun(text)) {
32800
+ this.deps.startMacro({
32801
+ text,
32802
+ id: -1
32803
+ }, false);
32804
+ return true;
32805
+ }
32806
+ const command = parseCommand(text);
32807
+ if (command !== null) {
32808
+ let bannered = false;
32809
+ const result = await executeCommand(command, this.deps.ctx(), (ok, note) => {
32810
+ bannered = true;
32811
+ this.noteCommand(ok ? "ok" : "error", note);
32812
+ });
32813
+ if (!bannered && result.note !== void 0) this.noteCommand(result.ok ? "ok" : "error", result.note);
32814
+ return true;
32815
+ }
32816
+ if (text.trim().length === 0 && images.length === 0) return false;
32817
+ const slashCatalog = this.deps.slashCatalog;
32818
+ if (slashCatalog?.state === "ready") {
32819
+ if (resolveHostCommand(text, slashCatalog.commands) !== null) return (await executeHostCommand(text, this.deps.ctx(), (ok, note) => this.noteCommand(ok ? "ok" : "error", note))).ok;
32820
+ }
32821
+ this.submitted = true;
32822
+ try {
32823
+ return (await sendPrompt(text, this.deps.ctx(), (t) => this.deps.orchestrator.submit(t, images))).ok;
32824
+ } finally {
32825
+ this.submitted = false;
32826
+ }
32827
+ }
32828
+ async oncancel() {
32829
+ await this.deps.orchestrator.cancel();
32830
+ }
32831
+ /** Menu command pick: the SAME rung the ladder uses (one execution
32832
+ * surface, D2), banner notes through the shared note path; the
32833
+ * boolean admits the composer's draft clearing (BC-A3). */
32834
+ async onpickcommand(line) {
32835
+ return (await executeHostCommand(line, this.deps.ctx(), (ok, note) => this.noteCommand(ok ? "ok" : "error", note))).ok;
32836
+ }
32837
+ };
32838
+ //#endregion
32839
+ //#region src/lib/utils/auto-open-slots.ts
32840
+ /** The newest reasoning entry's id (wire order), else null — the
32841
+ * transient think target. Empty reasoning strings do not count (the
32842
+ * chip renders nothing to open). */
32843
+ function newestReasoningId(entries) {
32844
+ for (let i = entries.length - 1; i >= 0; i--) {
32845
+ const e = entries[i];
32846
+ if (e.kind === "assistant-message" && e.reasoning !== void 0 && e.reasoning !== "") return e.id;
32847
+ }
32848
+ return null;
32849
+ }
32850
+ /** Suppression-aware view of a raw target: null while the user kept THIS
32851
+ * id collapsed (a newer target id simply stops matching). */
32852
+ function suppressed(targetId, suppressedId) {
32853
+ return targetId !== null && suppressedId === targetId ? null : targetId;
32854
+ }
32855
+ //#endregion
32856
+ //#region src/lib/components/conversation/panel/popups.svelte.ts
32857
+ var Popups = class {
32858
+ /** Chip popup state (2026-08-22, OCI pattern): ONE open chip at a time —
32859
+ * the id of the expanded entry; detail renders AFTER the chip row.
32860
+ * Per-panel by construction (moved here with the block). */
32861
+ openChipId = null;
32862
+ /** Peek popup state (ToolPeekButton port, 2026-08-22): the run key whose
32863
+ * chip row has its list popup open — ONE at a time, and never while a
32864
+ * chip detail popup is open (OCI mutual-exclusion contract). */
32865
+ peekOpenRunKey = null;
32866
+ thinkSuppressedId = null;
32867
+ planSuppressedId = null;
32868
+ /** The newest todo_write entry's id, STICKY: adopted only while
32869
+ * streaming AND stuck (the detection window), then kept across newer
32870
+ * thinks, turn end, and scroll-away — the plan stays on screen until
32871
+ * a newer todo_write supersedes it or the user collapses it. */
32872
+ autoPlanIdRaw = null;
32873
+ deps;
32874
+ stopPlanEffect;
32875
+ constructor(deps) {
32876
+ this.deps = deps;
32877
+ this.stopPlanEffect = () => {};
32878
+ }
32879
+ destroy() {
32880
+ this.stopPlanEffect();
32881
+ }
32882
+ /** The newest reasoning entry's id (wire order) while streaming AND
32883
+ * stuck to the live end, else null — the transient think target. */
32884
+ get autoThinkTargetId() {
32885
+ if (!this.deps.store.isStreaming || !this.deps.stick) return null;
32886
+ return newestReasoningId(this.deps.store.renderEntries);
32887
+ }
32888
+ /** Suppression-aware think target: null while the user kept THIS think
32889
+ * collapsed (a newer think id simply stops matching). */
32890
+ get autoThinkId() {
32891
+ return suppressed(this.autoThinkTargetId, this.thinkSuppressedId);
32892
+ }
32893
+ /** Suppression-aware plan target (same id-tied rule as the think). */
32894
+ get autoPlanId() {
32895
+ return suppressed(this.autoPlanIdRaw, this.planSuppressedId);
32896
+ }
32897
+ toggleChip(id, autoOpen = false) {
32898
+ if (autoOpen) {
32899
+ if (id === this.autoPlanId) this.planSuppressedId = id;
32900
+ else if (this.autoThinkId !== null && id === `r:${this.autoThinkId}`) this.thinkSuppressedId = this.autoThinkId;
32901
+ return;
32902
+ }
32903
+ this.openChipId = this.openChipId === id ? null : id;
32904
+ this.peekOpenRunKey = null;
32905
+ }
32906
+ togglePeek(runKey) {
32907
+ this.peekOpenRunKey = this.peekOpenRunKey === runKey ? null : runKey;
32908
+ if (this.peekOpenRunKey !== null) this.openChipId = null;
32909
+ }
32910
+ };
32911
+ //#endregion
32912
+ //#region src/lib/components/conversation/panel/paging.svelte.ts
31996
32913
  var Paging = class {
31997
32914
  hasOlder = false;
31998
32915
  loadingOlder = false;
@@ -33541,7 +34458,7 @@ function BrowserPanel($$renderer, $$props) {
33541
34458
  tab: t,
33542
34459
  disabled: switching});
33543
34460
  }
33544
- $$renderer.push(`<!--]--> <button type="button" class="shrink-0 rounded px-1.5 leading-none text-zinc-500 hover:bg-zinc-100 disabled:opacity-40" title="New tab" aria-label="New tab" data-testid="browser-tab-new"${attr("disabled", true, true)}>+</button></div>`);
34461
+ $$renderer.push(`<!--]--> <button type="button" class="shrink-0 rounded px-1.5 leading-none text-zinc-500 hover:bg-zinc-100 disabled:opacity-40"${attr("title", t(browsernewtab2))}${attr("aria-label", t(browsernewtab2))} data-testid="browser-tab-new"${attr("disabled", true, true)}>+</button></div>`);
33545
34462
  } else $$renderer.push("<!--[-1-->");
33546
34463
  $$renderer.push(`<!--]--> `);
33547
34464
  BrowserContent($$renderer, {
@@ -34427,9 +35344,13 @@ function WorkspaceFileTabGroup($$renderer, $$props) {
34427
35344
  //#region src/lib/components/workspace-file/WorkspaceFileToolbar.svelte
34428
35345
  function WorkspaceFileToolbar($$renderer, $$props) {
34429
35346
  $$renderer.component(($$renderer) => {
35347
+ /** The reload icon's feedback state — the CanvasCopyButton machine
35348
+ * (idle → busy spinner → done check → idle), mirrored per the 2026-10-27
35349
+ * UX-parity operator request: a click that shows nothing happening "sucks". */
35350
+ let reloadState = "idle";
34430
35351
  let columnEl = null;
34431
- let { dirty, saveState = "idle", stale = false, staleNote = "", hasTabs = false, tab = "preview", view = "edit", showToggle = false, diffDisabled = false, readonly = false, onviewchange, onsave, path } = $$props;
34432
- $$renderer.push(`<div data-svelte="WorkspaceFileToolbar" class="file-toolbar svelte-1vsjf39"><span class="path svelte-1vsjf39"${attr("title", path)}>${escape_html(path)}</span> `);
35352
+ let { dirty, saveState = "idle", stale = false, staleNote = "", hasTabs = false, tab = "preview", view = "edit", showToggle = false, diffDisabled = false, readonly = false, onviewchange, onsave, onreload, path } = $$props;
35353
+ $$renderer.push(`<div data-svelte="WorkspaceFileToolbar" class="file-toolbar svelte-1vsjf39" data-testid="file-toolbar"><span class="path svelte-1vsjf39"${attr("title", path)}>${escape_html(path)}</span> `);
34433
35354
  if (showToggle) {
34434
35355
  $$renderer.push("<!--[0-->");
34435
35356
  WorkspaceFileTabGroup($$renderer, {
@@ -34461,6 +35382,14 @@ function WorkspaceFileToolbar($$renderer, $$props) {
34461
35382
  $$renderer.push("<!--[0-->");
34462
35383
  $$renderer.push(`<div class="tabs svelte-1vsjf39" role="tablist"><button role="tab"${attr("aria-selected", tab === "preview")} data-testid="tab-preview"${attr_class("svelte-1vsjf39", void 0, { "active": tab === "preview" })}>${escape_html(t(workspacefilepreview2))}</button> <button role="tab"${attr("aria-selected", tab === "edit")} data-testid="tab-edit"${attr_class("svelte-1vsjf39", void 0, { "active": tab === "edit" })}>${escape_html(t(workspacefileedit2))}</button></div>`);
34463
35384
  } else $$renderer.push("<!--[-1-->");
35385
+ $$renderer.push(`<!--]--> `);
35386
+ if (onreload) {
35387
+ $$renderer.push("<!--[0-->");
35388
+ $$renderer.push(`<button type="button" class="reload-btn svelte-1vsjf39" data-testid="file-reload"${attr("data-state", reloadState)}${attr("title", t(refresh))}${attr("aria-label", t(refresh))}>`);
35389
+ $$renderer.push("<!--[-1-->");
35390
+ Rotate_cw($$renderer, { size: 14 });
35391
+ $$renderer.push(`<!--]--></button>`);
35392
+ } else $$renderer.push("<!--[-1-->");
34464
35393
  $$renderer.push(`<!--]--> <span class="copy-slot svelte-1vsjf39">`);
34465
35394
  CanvasCopyButton($$renderer, {
34466
35395
  container: columnEl,
@@ -34492,7 +35421,7 @@ function WorkspaceFile($$renderer, $$props) {
34492
35421
  * old overflow:auto container made the hosts content-sized and the
34493
35422
  * page-level scrollbar stole the height.
34494
35423
  */
34495
- let { tab = void 0, dirty, fullPath, saveState, fileStale, staleNote, hasTabs, activeView, showToggle, diffDisabled, readonly, onViewChange, onSave, saveError, saveFailedNote, loadingNote, loadFailedNote, truncatedNote, newFileNote, phase, errorCode, title, isImage, bytesSrc, isHtml, text, isMarkdown, noHead, eof, editorHost = null, diffHost = null } = $$props;
35424
+ let { tab = void 0, dirty, fullPath, saveState, fileStale, staleNote, hasTabs, activeView, showToggle, diffDisabled, readonly, onViewChange, onSave, onReload, saveError, saveFailedNote, loadingNote, loadFailedNote, truncatedNote, newFileNote, phase, errorCode, title, isImage, bytesSrc, isHtml, text, isMarkdown, noHead, eof, editorHost = null, diffHost = null } = $$props;
34496
35425
  let $$settled = true;
34497
35426
  let $$inner_renderer;
34498
35427
  function $$render_inner($$renderer) {
@@ -34510,6 +35439,7 @@ function WorkspaceFile($$renderer, $$props) {
34510
35439
  readonly,
34511
35440
  onviewchange: onViewChange,
34512
35441
  onsave: onSave,
35442
+ onreload: onReload,
34513
35443
  get tab() {
34514
35444
  return tab;
34515
35445
  },
@@ -34661,6 +35591,13 @@ function WorkspaceFilePanel($$renderer, $$props) {
34661
35591
  const homeDir = marker > 0 ? dsi.slice(0, marker) : null;
34662
35592
  return homeDir !== null && full.startsWith(homeDir + "/") ? "~" + full.slice(homeDir.length) : full;
34663
35593
  });
35594
+ /** Silent external refetch (Index Pulse): when the buffer holds no
35595
+ * draft, the fetched text/baseline advance to the disk truth and the
35596
+ * editor follows; when a draft exists, the panel only raises the
35597
+ * visible stale note - a draft is never clobbered. */
35598
+ async function refreshFile() {
35599
+ if (isImage() || true) return;
35600
+ }
34664
35601
  /** The DIRECT save (The File Eye ADR D4): POST the buffer to the
34665
35602
  * gated write route; the written receipt advances the baseline so
34666
35603
  * dirty clears — bytes on disk, not an agent's promise. */
@@ -34682,6 +35619,7 @@ function WorkspaceFilePanel($$renderer, $$props) {
34682
35619
  readonly: false,
34683
35620
  onViewChange: setView,
34684
35621
  onSave: save,
35622
+ onReload: refreshFile,
34685
35623
  saveError,
34686
35624
  saveFailedNote: t(workspacefilesavefailed3),
34687
35625
  loadingNote: t(workspaceexplorerloading2),
@@ -34925,8 +35863,10 @@ function WorkspaceExplorerPanel($$renderer, $$props) {
34925
35863
  * the active file panel and cleared once it reports the highlight done. */
34926
35864
  let lineTarget = null;
34927
35865
  function startSplitDrag(event) {}
34928
- const splitPct = derived(() => clampTreePct(treePct));
34929
- $$renderer.push(`<div data-svelte="WorkspaceExplorerPanel" data-testid="workspace-explorer" class="split svelte-ldghfc"${attr_style(`--tree-basis: ${stringify(splitPct())}%; --file-basis: ${stringify(100 - splitPct())}%`)}>`);
35866
+ /** The rendered tree lane: the settled px, or the narrow-lane default
35867
+ * before the first measurement. Percent lives only in treePct. */
35868
+ const treeBasis = derived(() => 280);
35869
+ $$renderer.push(`<div data-svelte="WorkspaceExplorerPanel" data-testid="workspace-explorer" class="split svelte-ldghfc"${attr_style(`--tree-basis: ${stringify(treeBasis())}px`)}>`);
34930
35870
  WorkspaceExplorer($$renderer, {
34931
35871
  activeFile,
34932
35872
  root,
@@ -35078,740 +36018,218 @@ function PanelHeader($$renderer, $$props) {
35078
36018
  class: "inline-flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded transition-colors hover:bg-surface-hover"
35079
36019
  });
35080
36020
  } else $$renderer.push("<!--[-1-->");
35081
- $$renderer.push(`<!--]--> `);
35082
- if (onremove || lens) {
35083
- $$renderer.push("<!--[0-->");
35084
- PanelClose($$renderer, {
35085
- sessionId,
35086
- lens});
35087
- } else $$renderer.push("<!--[-1-->");
35088
- $$renderer.push(`<!--]--></span></div>`);
35089
- });
35090
- }
35091
- //#endregion
35092
- //#region src/lib/components/panels/PanelColumn.svelte
35093
- function PanelColumn($$renderer, $$props) {
35094
- $$renderer.component(($$renderer) => {
35095
- /**
35096
- * PanelColumn — one panel's fixed-width column shell (Panel Floor W3
35097
- * task 3.2, ADR-0006): PanelHeader (floor chrome) + the conversation
35098
- * body (ConversationPanel, keyed by sessionId INSIDE the route's each
35099
- * — remount locality) + the 6px ResizeGutter after every panel.
35100
- *
35101
- * Presentational shell only: width and selection arrive as props
35102
- * (bound up to the route); the body arrives as a snippet so the ROUTE
35103
- * owns the {#key panel.sessionId} remount and the per-panel cold data.
35104
- *
35105
- * Width badge (2026-08-28): a `widthBadge` phase prop ('live' during
35106
- * a gutter drag that moves this panel's width, 'fading' through the
35107
- * 10s afterglow) floats the CURRENT width at the column's top right,
35108
- * below the header's controls, pointer-transparent — the route owns
35109
- * the membership rule (changed-from-snapshot) and the fade timer.
35110
- */
35111
- let { panel, index, selected, widthBadge = null, copyValue, canMoveLeft = false, canMoveRight = false, onremove, children } = $$props;
35112
- $$renderer.push(`<div data-svelte="PanelColumn"${attr_class("column svelte-1cuodug", void 0, { "selected": selected })} data-testid="panel-column"${attr("data-panel-id", panel.id)}${attr("data-session-id", panel.kind === "conversation" ? panel.sessionId : null)}${attr_style(`width: ${stringify(panel.width)}px; min-width: ${stringify(panel.width)}px; max-width: ${stringify(panel.width)}px;`)}>`);
35113
- PanelHeader($$renderer, {
35114
- panelId: panel.id,
35115
- sessionId: panel.kind === "conversation" ? panel.sessionId : "",
35116
- label: panelHeaderLabel(panel),
35117
- copyValue: panel.kind === "workspace-explorer" ? panel.root : panel.kind === "workspace-file" ? copyValue : void 0,
35118
- copyLabel: panel.kind === "workspace-explorer" ? t(workspaceexplorercopyworkspace3) : panel.kind === "workspace-file" ? t(workspacefilecopyfilename3) : void 0,
35119
- labelTestId: panel.kind === "workspace-explorer" ? "explorer-title" : panel.kind === "workspace-file" ? "file-title" : void 0,
35120
- selected,
35121
- canMoveLeft,
35122
- canMoveRight,
35123
- onremove
35124
- });
35125
- $$renderer.push(`<!----> `);
35126
- if (widthBadge !== null) {
35127
- $$renderer.push("<!--[0-->");
35128
- $$renderer.push(`<span${attr_class("width-badge svelte-1cuodug", void 0, { "fading": widthBadge === "fading" })} data-testid="panel-width-badge"${attr("data-badge-phase", widthBadge)} aria-hidden="true">${escape_html(Math.round(panel.width))}px</span>`);
35129
- } else $$renderer.push("<!--[-1-->");
35130
- $$renderer.push(`<!--]--> <div class="body svelte-1cuodug">`);
35131
- children($$renderer);
35132
- $$renderer.push(`<!----></div></div> `);
35133
- ResizeGutter($$renderer, { index });
35134
- $$renderer.push(`<!---->`);
35135
- });
35136
- }
35137
- //#endregion
35138
- //#region src/lib/components/panels/PanelLoupe.svelte
35139
- function PanelLoupe($$renderer, $$props) {
35140
- $$renderer.component(($$renderer) => {
35141
- /**
35142
- * PanelLoupe — the reading lens over one floor panel (The Panel Loupe
35143
- * ADR D2/D5, 2026-09-04): a body-portaled modal dialog that renders
35144
- * one panel's content at reading size while the floor keeps its
35145
- * zoom. ANY kind lenses (The Loupe for Every Panel, 2026-09-08): the
35146
- * route feeds the SAME body snippet the column renders (D7 — one
35147
- * ladder, two sites) and the header rides the panel's label variant;
35148
- * the loupe itself carries no conversation logic.
35149
- *
35150
- * Contract donor: ImageLightbox (2026-08-28) — the portal, the fixed
35151
- * inset-0 z-[1000] layer, role="dialog" aria-modal, the mask's
35152
- * mousedown close, Escape, the × control, focus to the × on open and
35153
- * back to the opener on close. The portal is load-bearing: the floor
35154
- * row carries transform: scale(), and a fixed backdrop inside a
35155
- * transformed ancestor is trapped in that ancestor's box instead of
35156
- * covering the viewport (ADR D2). The action is inlined here by
35157
- * decision — the donor is cited, not modified or refactored.
35158
- *
35159
- * Lens flag (D8): setLensMode(true) during init, BEFORE children
35160
- * render — every floor-verb control inside the body snippet reads the
35161
- * flag at its own init and renders visible-disabled. Context follows
35162
- * the component tree, so the flag reaches snippet content even after
35163
- * the portal moves the DOM under document.body (asserted by the loupe
35164
- * unit tests).
35165
- *
35166
- * The lens × and this header's copy-id are the loupe's live controls;
35167
- * the header's move/close render visible-disabled (the floor verbs).
35168
- * Openness is the route's session-local state — never persisted (D5).
35169
- */
35170
- let { panelId, sessionId, label = void 0, children } = $$props;
35171
- setLensMode(true);
35172
- $$renderer.push(`<div data-svelte="PanelLoupe" class="fixed inset-0 z-[1000] grid place-items-center p-10" role="dialog" aria-modal="true"${attr("aria-label", t(panelloupe1))} data-testid="panel-loupe"${attr("data-panel-id", panelId)}${attr("data-session-id", sessionId)}><div class="absolute inset-0 bg-black/60" aria-hidden="true" data-testid="panel-loupe-mask"></div> <div class="relative" style="width: min(100vw - 4rem, 850px); height: min(95vh, calc(100vh - 5rem));" data-testid="panel-loupe-sheet"><div class="flex h-full w-full flex-col overflow-hidden rounded-xl shadow-2xl" style="background: var(--color-surface-primary, var(--color-surface-elevated));">`);
35173
- PanelHeader($$renderer, {
35174
- panelId,
35175
- sessionId,
35176
- label,
35177
- lens: true
35178
- });
35179
- $$renderer.push(`<!----> <div class="flex min-h-0 flex-1 flex-col overflow-hidden">`);
35180
- children($$renderer);
35181
- $$renderer.push(`<!----></div></div> <button type="button"${attr("aria-label", t(closeloupe1))} data-testid="panel-loupe-close" class="absolute -top-3 -right-3 z-10 flex h-9 w-9 cursor-pointer items-center justify-center rounded-full border border-surface-border bg-surface-elevated text-lg leading-none text-text-primary shadow-md hover:bg-surface-hover">×</button></div></div>`);
35182
- });
35183
- }
35184
- //#endregion
35185
- //#region src/lib/components/panels/StickyColumnContainer.svelte
35186
- function StickyColumnContainer($$renderer, $$props) {
35187
- $$renderer.component(($$renderer) => {
35188
- /**
35189
- * StickyColumnContainer — a fixed-width LEADING column inside the zoom
35190
- * row (OCI StickyColumnContainer port, 2026-08-26; hosts the embedded
35191
- * AppSidebar the way OCI's hosts ControlRail).
35192
- *
35193
- * "Sticky" = the width is TRIPLE-LOCKED (width/min/max all the same
35194
- * px), unlike panel columns whose widths live in [MIN, MAX]: the rail
35195
- * never flexes when the row measures itself at max-content. The
35196
- * column's background paints the full column height — the rail inside
35197
- * fills it (align-items: stretch).
35198
- *
35199
- * The trailing ResizeGutter is included BY DEFAULT (OCI parity) but
35200
- * only when panel columns actually follow (showGutter) — a rail alone
35201
- * on the floor has nothing to resize against. The gutter is the
35202
- * column's flex SIBLING (the rail's overflow must never clip its hit
35203
- * area) and reports mousedown through onResizeStart — the page owns
35204
- * the drag math and the single clamp (AppSidebar ownership split;
35205
- * ControlBar.onresizeall precedent: layout chrome is a DIRECT child
35206
- * of the route, so prop wiring here breaks no commitment).
35207
- */
35208
- let { width, showGutter = true, children } = $$props;
35209
- $$renderer.push(`<div data-svelte="StickyColumnContainer" class="rail-column svelte-s09nms" data-testid="sidebar-column"${attr_style(`width: ${stringify(width)}px; min-width: ${stringify(width)}px; max-width: ${stringify(width)}px;`)}>`);
35210
- children($$renderer);
35211
- $$renderer.push(`<!----></div> `);
35212
- if (showGutter) {
35213
- $$renderer.push("<!--[0-->");
35214
- $$renderer.push(`<div data-svelte="StickyColumnContainer" class="rail-gutter svelte-s09nms" data-testid="sidebar-gutter" role="separator" aria-orientation="vertical"${attr("aria-label", t(resizesidebar1))}></div>`);
35215
- } else $$renderer.push("<!--[-1-->");
35216
- $$renderer.push(`<!--]-->`);
35217
- });
35218
- }
35219
- //#endregion
35220
- //#region src/lib/components/panels/EmptyFloor.svelte
35221
- function EmptyFloor($$renderer, $$props) {
35222
- $$renderer.component(($$renderer) => {
35223
- $$renderer.push(`<div data-svelte="EmptyFloor" class="empty-floor svelte-z8g5d9" data-testid="workspace-empty"><p>${escape_html(t(noconversations1))}</p> <p class="hint svelte-z8g5d9">${escape_html(t(picksession1))}</p></div>`);
35224
- });
35225
- }
35226
- //#endregion
35227
- //#region src/lib/services/conversation/spine-feed.svelte.ts
35228
- var spineFeed = {
35229
- rows: [],
35230
- presets: [],
35231
- a2aRows: [],
35232
- workspaces: []
35233
- };
35234
- //#endregion
35235
- //#region src/lib/services/workspace/page-feeds.svelte.ts
35236
- var PageFeeds = class {
35237
- coldCache = {};
35238
- coldInFlight = new SvelteSet();
35239
- deadSessions = {};
35240
- edgeCache = {};
35241
- /** Seed-arrival prime: the seed panel mounts with full cold props on
35242
- * paint one (SSR parity for the seed path). Load-time constant. */
35243
- primeSeed(sessionId, cold) {
35244
- this.coldCache = {
35245
- ...this.coldCache,
35246
- [sessionId]: cold
35247
- };
35248
- }
35249
- /** Record an ADD-time parent edge (the route's doAdd fork path). */
35250
- rememberEdge(childSessionId, parentSessionId) {
35251
- this.edgeCache = {
35252
- ...this.edgeCache,
35253
- [childSessionId]: parentSessionId
35254
- };
35255
- }
35256
- /** One dual fetch, registries kept honest (in-flight guard, dead
35257
- * session card, loader clear in `finally`). The fetcher is injected:
35258
- * the wire call stays the route's (`fetchCold` reads the host
35259
- * config); the bookkeeping lives here. */
35260
- async runCold(sessionId, fetchCold) {
35261
- if (this.coldCache[sessionId] !== void 0 || this.coldInFlight.has(sessionId)) return;
35262
- this.coldInFlight = new SvelteSet(this.coldInFlight).add(sessionId);
35263
- try {
35264
- const cold = await fetchCold(sessionId);
35265
- if (cold !== null) this.coldCache = {
35266
- ...this.coldCache,
35267
- [sessionId]: cold
35268
- };
35269
- else this.deadSessions = {
35270
- ...this.deadSessions,
35271
- [sessionId]: `Session ${sessionId} not found on the DSH host. It may have been reaped.`
35272
- };
35273
- } finally {
35274
- const next = new SvelteSet(this.coldInFlight);
35275
- next.delete(sessionId);
35276
- this.coldInFlight = next;
35277
- }
35278
- }
35279
- /** The floor's two lifecycle feeds (call during component init —
35280
- * `$effect` needs an owner):
35281
- * - Prompt Sync hygiene (ADR "The Prompt Sync" D1/D6): a session
35282
- * that left the floor holds no check and no snapshot; count 0
35283
- * clears the broadcast. Conversation sessions only (D6).
35284
- * - Spine feed lifecycle (W8): ref-counted single timer started on
35285
- * mount, stopped on destroy; the a2a window rides the floor's
35286
- * conversation senders (first sender per tick, never per panel).
35287
- * Returns the spine subscription's cleanup. */
35288
- installFeeds(getConversationSessionIds) {
35289
- return () => {};
35290
- }
35291
- };
35292
- var pageFeeds = new PageFeeds();
35293
- /**
35294
- * The open workspace explorer for a workspace ROOT — The Shared Tree ADR
35295
- * (2026-09-17) D1's dedupe key: ONE explorer per ROOT across ALL sessions,
35296
- * so a chip click on any session sharing the root FOCUSES instead of
35297
- * adding (a duplicate is a bug, not a feature). The retired per-session
35298
- * key ("two sessions sharing one workspace hold distinct explorers",
35299
- * Workspace Explorer D4) produced N byte-identical trees for N sessions.
35300
- */
35301
- function findWorkspaceExplorerPanel(panels, root) {
35302
- return panels.find((p) => p.kind === "workspace-explorer" && p.root === root);
35303
- }
35304
- /**
35305
- * The explorer's slot (The Shared Tree ADR 2026-09-17, D3): the FOCUSED
35306
- * slot — the explorer is NOT a lineage child; a workspace tree has no
35307
- * parent session, only an opener. The retired after-source rule
35308
- * (Workspace Explorer D3, "Parent → explorer → file") interposed the
35309
- * panel inside a conversation's family unit — the family clamp inside
35310
- * focusedInsertionSlot still keeps any real family unsplit.
35311
- */
35312
- function workspaceExplorerSlot(panels, selectedIndex, facts) {
35313
- return focusedInsertionSlot(panels, selectedIndex, facts.parentOf);
35314
- }
35315
- /**
35316
- * The glass FIRST slot under its opener (operator ruling 2026-10-24):
35317
- * a summoned/spawned glass lands DIRECTLY below its opener - past the
35318
- * opener document tail, BEFORE every adopted child - matching the
35319
- * spine tree wire order -1 (the ghost always reads as the first
35320
- * child). Supersedes the region-end placement for the glass only.
35321
- */
35322
- function glassFirstSlot(panels, openedBy) {
35323
- const openerIdx = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === openedBy);
35324
- if (openerIdx === -1) return panels.length;
35325
- return docTailEnd(panels, openerIdx, openedBy) + 1;
35326
- }
35327
- /**
35328
- * Insert-before-focused slot (operator spec, 2026-08-31): a fresh panel
35329
- * lands at the FOCUSED panel's index — immediately before it — and takes
35330
- * focus (the route selects it). 0 when nothing is focused (empty floor,
35331
- * stale selection) — the old front-add stays the fallback. Math.max keeps
35332
- * splice's negative-index semantics out of the no-selection case.
35333
- *
35334
- * Family clamp (lineage I2): a slot strictly inside a family would wedge
35335
- * the newcomer between a spawner and its pinned child — unitOf/blockBounds,
35336
- * the sidebar's familyRenderOrder, and the adoption slot walk all assume
35337
- * families stay contiguous on the floor. When the focus is a family CHILD,
35338
- * the slot climbs to the family head — the nearest non-splitting slot left
35339
- * of the focus (parentOf only carries on-floor parents, so an orphan child
35340
- * is a root here, its own unit). A lone panel or a family head keeps the
35341
- * exact focus slot.
35342
- *
35343
- * Manager focus (ADR D6): no session, no family — the slot is the focus
35344
- * itself.
35345
- *
35346
- * Workspace member focus (2026-09-10): the explorer/file pair is a family
35347
- * unit in the SAME block math (blockBounds — the Loadinjected D3 walk-back
35348
- * grammar), so the clamp is the SAME one: insert at the unit head — the
35349
- * PARENT position, left of the block — never between explorer and file.
35350
- */
35351
- function focusedInsertionSlot(panels, selectedIndex, parentOf) {
35352
- const focus = Math.max(0, selectedIndex);
35353
- const focused = panels[focus];
35354
- if (focused === void 0) return 0;
35355
- if (focused.kind === "workspace-explorer" || focused.kind === "workspace-file" || focused.kind === "browser") {
35356
- const block = blockBounds(panels, focus, { parentOf });
35357
- return block === null ? focus : block[0];
35358
- }
35359
- if (focused.kind !== "conversation") return focus;
35360
- let head = focused.sessionId;
35361
- for (;;) {
35362
- const parent = parentOf.get(head) ?? null;
35363
- if (parent === null) break;
35364
- head = parent;
35365
- }
35366
- if (head === focused.sessionId) return focus;
35367
- const headIndex = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === head);
35368
- return headIndex >= 0 ? Math.min(headIndex, focus) : focus;
35369
- }
35370
- //#endregion
35371
- //#region src/lib/services/panels/floor-registry/chrome.ts
35372
- /**
35373
- * floor-registry/chrome — panel chrome intents + explorer + file-intent
35374
- * mailbox + removal (ADR-0022 W3).
35375
- *
35376
- * Moved WHOLE from the state home (floor-registry.svelte.ts): the
35377
- * skill-shelf / plugin-rack chrome setters, the ONE-explorer-per-workspace
35378
- * open grammar, the explorer tree/tab/git intents, the file-intent
35379
- * publish/consume mailbox, and family-close removal. Plain module: every
35380
- * write goes through the state home's reactive `st` hub and its
35381
- * primitives (ADR-0019 D2: exactly ONE writer home for floor state).
35382
- * Sibling rule: factories/slots are reached through the state home's
35383
- * re-export hub, never by importing another sibling directly.
35384
- */
35385
- var d$4 = floorSeam;
35386
- /** Chip click action: ONE explorer per WORKSPACE root across ALL
35387
- * sessions — a click on any session sharing the root FOCUSES the open
35388
- * explorer (dedupe key = root, D1); a fresh root lands at the focused
35389
- * slot (D3, non-lineage — the explorer is not a lineage child).
35390
- * sessionId rides the fresh panel as PROVENANCE (D4), never identity. */
35391
- function openWorkspaceExplorer(sessionId, workspace) {
35392
- const open = findWorkspaceExplorerPanel(st.panels, workspace);
35393
- if (open) {
35394
- st.selectedPanelId = open.id;
35395
- return;
35396
- }
35397
- insertPanel(makeWorkspaceExplorerPanel(sessionId, workspace), workspaceExplorerSlot(st.panels, selectedIndex_(), d$4().getMoveFacts()));
35398
- }
35399
- /** Chip click action (The Glass Browser spec W2, ADR-0029 D4): ONE live
35400
- * browser panel across the floor — the terminal singleton rule (Terminal
35401
- * Desk ADR D2). A click on an open browser FOCUSES it; a miss inserts at
35402
- * the focused slot. */
35403
- function openBrowserPanel(index, keepSelection = false, width, openedBy, sessionId) {
35404
- const open = st.panels.find((p) => p.kind === "browser" && p.openedBy === openedBy);
35405
- if (open) {
35406
- st.selectedPanelId = open.id;
35407
- return;
35408
- }
35409
- const prevSelected = st.selectedPanelId;
35410
- const slot = index !== void 0 ? Math.max(0, Math.min(index, st.panels.length)) : openedBy !== void 0 ? glassFirstSlot(st.panels, openedBy) : insertionSlot();
35411
- insertPanel(makeBrowserPanel(width, openedBy, sessionId), slot);
35412
- if (keepSelection && prevSelected !== null && prevSelected !== void 0) {
35413
- if (st.panels.some((p) => p.id === prevSelected)) st.selectedPanelId = prevSelected;
35414
- }
35415
- }
35416
- /** The open explorer for a session's workspace (The Shared Tree D1):
35417
- * matched by ROOT across sessions — the panel's provenance sessionId
35418
- * is not the key. */
35419
- function sessionWorkspaceExplorer(sessionId) {
35420
- const root = d$4().getWorkspaceFor(sessionId);
35421
- return root === null ? void 0 : findWorkspaceExplorerPanel(st.panels, root);
35422
- }
35423
- /** Explorer tree intents: the panel emits toggle/open-file; the owner
35424
- * mutates expanded (the persisted part) — the levels cache stays
35425
- * component-local. */
35426
- function toggleExplorerPath(panelId, path) {
35427
- const idx = st.panels.findIndex((p) => p.id === panelId);
35428
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35429
- const explorer = st.panels[idx];
35430
- const next = explorer.expanded.includes(path) ? explorer.expanded.filter((p) => p !== path) : [...explorer.expanded, path];
35431
- st.panels = st.panels.with(idx, {
35432
- ...explorer,
35433
- expanded: next
35434
- });
35435
- }
35436
- /** Explorer collapse-all intent: the panel emits; the owner empties the
35437
- * expanded list (the persisted part). */
35438
- function collapseExplorerPaths(panelId) {
35439
- const idx = st.panels.findIndex((p) => p.id === panelId);
35440
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35441
- st.panels = st.panels.with(idx, {
35442
- ...st.panels[idx],
35443
- expanded: []
35444
- });
35445
- }
35446
- /** Git Eye tab intent (2026-09-12): the active tab is PERSISTED on the
35447
- * entry — a hard reload restores it. */
35448
- function setExplorerTab(panelId, tab) {
35449
- const idx = st.panels.findIndex((p) => p.id === panelId);
35450
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35451
- st.panels = st.panels.with(idx, {
35452
- ...st.panels[idx],
35453
- tab
35454
- });
35455
- }
35456
- /** Explorer Layout tabs (amendment 2026-09-16): the tab list and the
35457
- * active tab are OWNER-PERSISTED entry facts. The file tabs die with
35458
- * the panel (D5). */
35459
- function openExplorerTab(panelId, path) {
35460
- const idx = st.panels.findIndex((p) => p.id === panelId);
35461
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35462
- const ex = st.panels[idx];
35463
- const tabs = ex.openTabs ?? [];
35464
- const openTabs = tabs.includes(path) ? tabs : [...tabs, path];
35465
- st.panels = st.panels.with(idx, {
35466
- ...ex,
35467
- openTabs,
35468
- activeFile: path
35469
- });
35470
- }
35471
- function closeExplorerTab(panelId, path) {
35472
- const idx = st.panels.findIndex((p) => p.id === panelId);
35473
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35474
- const ex = st.panels[idx];
35475
- const tabs = ex.openTabs ?? [];
35476
- const closedAt = tabs.indexOf(path);
35477
- if (closedAt === -1) return;
35478
- const openTabs = tabs.filter((p) => p !== path);
35479
- const activeFile = ex.activeFile === path ? openTabs.length === 0 ? null : openTabs[Math.max(0, closedAt - 1)] : ex.activeFile ?? null;
35480
- st.panels = st.panels.with(idx, {
35481
- ...ex,
35482
- openTabs,
35483
- activeFile
36021
+ $$renderer.push(`<!--]--> `);
36022
+ if (onremove || lens) {
36023
+ $$renderer.push("<!--[0-->");
36024
+ PanelClose($$renderer, {
36025
+ sessionId,
36026
+ lens});
36027
+ } else $$renderer.push("<!--[-1-->");
36028
+ $$renderer.push(`<!--]--></span></div>`);
35484
36029
  });
35485
36030
  }
35486
- function activateExplorerTab(panelId, path) {
35487
- const idx = st.panels.findIndex((p) => p.id === panelId);
35488
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35489
- const ex = st.panels[idx];
35490
- if (!(ex.openTabs ?? []).includes(path)) return;
35491
- st.panels = st.panels.with(idx, {
35492
- ...ex,
35493
- activeFile: path
36031
+ //#endregion
36032
+ //#region src/lib/components/panels/PanelColumn.svelte
36033
+ function PanelColumn($$renderer, $$props) {
36034
+ $$renderer.component(($$renderer) => {
36035
+ /**
36036
+ * PanelColumn — one panel's fixed-width column shell (Panel Floor W3
36037
+ * task 3.2, ADR-0006): PanelHeader (floor chrome) + the conversation
36038
+ * body (ConversationPanel, keyed by sessionId INSIDE the route's each
36039
+ * — remount locality) + the 6px ResizeGutter after every panel.
36040
+ *
36041
+ * Presentational shell only: width and selection arrive as props
36042
+ * (bound up to the route); the body arrives as a snippet so the ROUTE
36043
+ * owns the {#key panel.sessionId} remount and the per-panel cold data.
36044
+ *
36045
+ * Width badge (2026-08-28): a `widthBadge` phase prop ('live' during
36046
+ * a gutter drag that moves this panel's width, 'fading' through the
36047
+ * 10s afterglow) floats the CURRENT width at the column's top right,
36048
+ * below the header's controls, pointer-transparent — the route owns
36049
+ * the membership rule (changed-from-snapshot) and the fade timer.
36050
+ */
36051
+ let { panel, index, selected, widthBadge = null, copyValue, canMoveLeft = false, canMoveRight = false, onremove, children } = $$props;
36052
+ $$renderer.push(`<div data-svelte="PanelColumn"${attr_class("column svelte-1cuodug", void 0, { "selected": selected })} data-testid="panel-column"${attr("data-panel-id", panel.id)}${attr("data-session-id", panel.kind === "conversation" ? panel.sessionId : null)}${attr_style(`width: ${stringify(panel.width)}px; min-width: ${stringify(panel.width)}px; max-width: ${stringify(panel.width)}px;`)}>`);
36053
+ PanelHeader($$renderer, {
36054
+ panelId: panel.id,
36055
+ sessionId: panel.kind === "conversation" ? panel.sessionId : "",
36056
+ label: panelHeaderLabel(panel),
36057
+ copyValue: panel.kind === "workspace-explorer" ? panel.root : panel.kind === "workspace-file" ? copyValue : void 0,
36058
+ copyLabel: panel.kind === "workspace-explorer" ? t(workspaceexplorercopyworkspace3) : panel.kind === "workspace-file" ? t(workspacefilecopyfilename3) : void 0,
36059
+ labelTestId: panel.kind === "workspace-explorer" ? "explorer-title" : panel.kind === "workspace-file" ? "file-title" : void 0,
36060
+ selected,
36061
+ canMoveLeft,
36062
+ canMoveRight,
36063
+ onremove
36064
+ });
36065
+ $$renderer.push(`<!----> `);
36066
+ if (widthBadge !== null) {
36067
+ $$renderer.push("<!--[0-->");
36068
+ $$renderer.push(`<span${attr_class("width-badge svelte-1cuodug", void 0, { "fading": widthBadge === "fading" })} data-testid="panel-width-badge"${attr("data-badge-phase", widthBadge)} aria-hidden="true">${escape_html(Math.round(panel.width))}px</span>`);
36069
+ } else $$renderer.push("<!--[-1-->");
36070
+ $$renderer.push(`<!--]--> <div class="body svelte-1cuodug">`);
36071
+ children($$renderer);
36072
+ $$renderer.push(`<!----></div></div> `);
36073
+ ResizeGutter($$renderer, { index });
36074
+ $$renderer.push(`<!---->`);
35494
36075
  });
35495
36076
  }
35496
- /** The Explorer Layout drag settle (ADR D4): one DESK fact — the split
35497
- * is desk state, persisted with the floor blob, NOT per entry. The
35498
- * panel already clamped; clamp again at the single bound site. */
35499
- function setExplorerTreePct(_panelId, pct) {
35500
- d$4().setTreePct(clampTreePct(pct));
35501
- }
35502
- /** Git Eye repo-collapse intent (2026-09-13): the collapsed repo set is
35503
- * PERSISTED on the entry. Default is EXPANDED. */
35504
- function toggleExplorerRepo(panelId, repoRel) {
35505
- const idx = st.panels.findIndex((p) => p.id === panelId);
35506
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35507
- const explorer = st.panels[idx];
35508
- const prev = explorer.collapsedRepos ?? [];
35509
- const next = prev.includes(repoRel) ? prev.filter((r) => r !== repoRel) : [...prev, repoRel];
35510
- st.panels = st.panels.with(idx, {
35511
- ...explorer,
35512
- collapsedRepos: next
36077
+ //#endregion
36078
+ //#region src/lib/components/panels/PanelLoupe.svelte
36079
+ function PanelLoupe($$renderer, $$props) {
36080
+ $$renderer.component(($$renderer) => {
36081
+ /**
36082
+ * PanelLoupe — the reading lens over one floor panel (The Panel Loupe
36083
+ * ADR D2/D5, 2026-09-04): a body-portaled modal dialog that renders
36084
+ * one panel's content at reading size while the floor keeps its
36085
+ * zoom. ANY kind lenses (The Loupe for Every Panel, 2026-09-08): the
36086
+ * route feeds the SAME body snippet the column renders (D7 — one
36087
+ * ladder, two sites) and the header rides the panel's label variant;
36088
+ * the loupe itself carries no conversation logic.
36089
+ *
36090
+ * Contract donor: ImageLightbox (2026-08-28) — the portal, the fixed
36091
+ * inset-0 z-[1000] layer, role="dialog" aria-modal, the mask's
36092
+ * mousedown close, Escape, the × control, focus to the × on open and
36093
+ * back to the opener on close. The portal is load-bearing: the floor
36094
+ * row carries transform: scale(), and a fixed backdrop inside a
36095
+ * transformed ancestor is trapped in that ancestor's box instead of
36096
+ * covering the viewport (ADR D2). The action is inlined here by
36097
+ * decision — the donor is cited, not modified or refactored.
36098
+ *
36099
+ * Lens flag (D8): setLensMode(true) during init, BEFORE children
36100
+ * render — every floor-verb control inside the body snippet reads the
36101
+ * flag at its own init and renders visible-disabled. Context follows
36102
+ * the component tree, so the flag reaches snippet content even after
36103
+ * the portal moves the DOM under document.body (asserted by the loupe
36104
+ * unit tests).
36105
+ *
36106
+ * The lens × and this header's copy-id are the loupe's live controls;
36107
+ * the header's move/close render visible-disabled (the floor verbs).
36108
+ * Openness is the route's session-local state — never persisted (D5).
36109
+ */
36110
+ let { panelId, sessionId, label = void 0, children } = $$props;
36111
+ setLensMode(true);
36112
+ $$renderer.push(`<div data-svelte="PanelLoupe" class="fixed inset-0 z-[1000] grid place-items-center p-10" role="dialog" aria-modal="true"${attr("aria-label", t(panelloupe1))} data-testid="panel-loupe"${attr("data-panel-id", panelId)}${attr("data-session-id", sessionId)}><div class="absolute inset-0 bg-black/60" aria-hidden="true" data-testid="panel-loupe-mask"></div> <div class="relative" style="width: min(100vw - 4rem, 850px); height: min(95vh, calc(100vh - 5rem));" data-testid="panel-loupe-sheet"><div class="flex h-full w-full flex-col overflow-hidden rounded-xl shadow-2xl" style="background: var(--color-surface-primary, var(--color-surface-elevated));">`);
36113
+ PanelHeader($$renderer, {
36114
+ panelId,
36115
+ sessionId,
36116
+ label,
36117
+ lens: true
36118
+ });
36119
+ $$renderer.push(`<!----> <div class="flex min-h-0 flex-1 flex-col overflow-hidden">`);
36120
+ children($$renderer);
36121
+ $$renderer.push(`<!----></div></div> <button type="button"${attr("aria-label", t(closeloupe1))} data-testid="panel-loupe-close" class="absolute -top-3 -right-3 z-10 flex h-9 w-9 cursor-pointer items-center justify-center rounded-full border border-surface-border bg-surface-elevated text-lg leading-none text-text-primary shadow-md hover:bg-surface-hover">×</button></div></div>`);
35513
36122
  });
35514
36123
  }
35515
- /** Git Eye collapse-all-repos intent (2026-09-13): collapses EVERY known
35516
- * repo — persisted like the single-repo toggle. */
35517
- function collapseExplorerRepos(panelId, repos) {
35518
- const idx = st.panels.findIndex((p) => p.id === panelId);
35519
- if (idx < 0 || st.panels[idx].kind !== "workspace-explorer") return;
35520
- const explorer = st.panels[idx];
35521
- const prev = explorer.collapsedRepos ?? [];
35522
- const next = [...prev, ...repos.filter((r) => !prev.includes(r))];
35523
- st.panels = st.panels.with(idx, {
35524
- ...explorer,
35525
- collapsedRepos: next
36124
+ //#endregion
36125
+ //#region src/lib/components/panels/StickyColumnContainer.svelte
36126
+ function StickyColumnContainer($$renderer, $$props) {
36127
+ $$renderer.component(($$renderer) => {
36128
+ /**
36129
+ * StickyColumnContainer — a fixed-width LEADING column inside the zoom
36130
+ * row (OCI StickyColumnContainer port, 2026-08-26; hosts the embedded
36131
+ * AppSidebar the way OCI's hosts ControlRail).
36132
+ *
36133
+ * "Sticky" = the width is TRIPLE-LOCKED (width/min/max all the same
36134
+ * px), unlike panel columns whose widths live in [MIN, MAX]: the rail
36135
+ * never flexes when the row measures itself at max-content. The
36136
+ * column's background paints the full column height — the rail inside
36137
+ * fills it (align-items: stretch).
36138
+ *
36139
+ * The trailing ResizeGutter is included BY DEFAULT (OCI parity) but
36140
+ * only when panel columns actually follow (showGutter) — a rail alone
36141
+ * on the floor has nothing to resize against. The gutter is the
36142
+ * column's flex SIBLING (the rail's overflow must never clip its hit
36143
+ * area) and reports mousedown through onResizeStart — the page owns
36144
+ * the drag math and the single clamp (AppSidebar ownership split;
36145
+ * ControlBar.onresizeall precedent: layout chrome is a DIRECT child
36146
+ * of the route, so prop wiring here breaks no commitment).
36147
+ */
36148
+ let { width, showGutter = true, children } = $$props;
36149
+ $$renderer.push(`<div data-svelte="StickyColumnContainer" class="rail-column svelte-s09nms" data-testid="sidebar-column"${attr_style(`width: ${stringify(width)}px; min-width: ${stringify(width)}px; max-width: ${stringify(width)}px;`)}>`);
36150
+ children($$renderer);
36151
+ $$renderer.push(`<!----></div> `);
36152
+ if (showGutter) {
36153
+ $$renderer.push("<!--[0-->");
36154
+ $$renderer.push(`<div data-svelte="StickyColumnContainer" class="rail-gutter svelte-s09nms" data-testid="sidebar-gutter" role="separator" aria-orientation="vertical"${attr("aria-label", t(resizesidebar1))}></div>`);
36155
+ } else $$renderer.push("<!--[-1-->");
36156
+ $$renderer.push(`<!--]-->`);
35526
36157
  });
35527
36158
  }
35528
- /** File Eye view intent (2026-09-13): diff vs edit is PERSISTED on the
35529
- * entry. */
35530
- function setFileView(panelId, view) {
35531
- const idx = st.panels.findIndex((p) => p.id === panelId);
35532
- if (idx < 0 || st.panels[idx].kind !== "workspace-file") return;
35533
- st.panels = st.panels.with(idx, {
35534
- ...st.panels[idx],
35535
- view
36159
+ //#endregion
36160
+ //#region src/lib/components/panels/EmptyFloor.svelte
36161
+ function EmptyFloor($$renderer, $$props) {
36162
+ $$renderer.component(($$renderer) => {
36163
+ $$renderer.push(`<div data-svelte="EmptyFloor" class="empty-floor svelte-z8g5d9" data-testid="workspace-empty"><p>${escape_html(t(noconversations1))}</p> <p class="hint svelte-z8g5d9">${escape_html(t(picksession1))}</p></div>`);
35536
36164
  });
35537
36165
  }
35538
- /** Tree file-click action (The Settings Tree ADR 2026-09-18 D1): the
35539
- * intent publishes to the session's explorer, which opens/focuses a
35540
- * tab. No open explorer ⇒ the intent drops. */
35541
- function openWorkspaceFile(sessionId, path, lines) {
35542
- const host = sessionWorkspaceExplorer(sessionId);
35543
- if (host) publishFileIntent(host.id, path, lines);
35544
- }
35545
- /** Publish a file intent to an explorer panel and focus it. `lines` is
35546
- * the optional #L anchor target — display metadata, not persisted. */
35547
- function publishFileIntent(explorerId, path, lines) {
35548
- st.pendingFileIntents[explorerId] = {
35549
- path,
35550
- nonce: ++st.fileIntentSeq,
35551
- lines: lines ?? null
35552
- };
35553
- st.selectedPanelId = explorerId;
35554
- }
35555
- /** The panel consumed intent nonce N — clear exactly that publish. */
35556
- function consumeFileIntent(explorerId, nonce) {
35557
- if (st.pendingFileIntents[explorerId]?.nonce === nonce) st.pendingFileIntents[explorerId] = null;
35558
- }
35559
- /** Remove a panel; family close walks the wire lineage transitively
35560
- * (every descendant sub-agent goes with it, nested included). The
35561
- * Shift+Click amendment (2026-09-01): family === false opts OUT — only
35562
- * THAT panel drops, the children re-root as orphan family heads.
35563
- *
35564
- * The session's workspace pair closes with it (2026-09-10); closing an
35565
- * EXPLORER dooms its file children BY PANEL ID — the stored
35566
- * explorerPanelId edge, with the legacy null-edge fallback. A
35567
- * settings-home explorer (sessionId null) dies with the close. */
35568
- function removePanel(panelId, opts = {}) {
35569
- const idx = st.panels.findIndex((p) => p.id === panelId);
35570
- if (idx < 0) return;
35571
- const root = st.panels[idx];
35572
- const rootSessionId = root.kind === "conversation" ? root.sessionId : null;
35573
- const doomed = new SvelteSet(rootSessionId !== null ? [rootSessionId] : []);
35574
- if (opts.family !== false && rootSessionId !== null) {
35575
- const stack = [rootSessionId];
35576
- while (stack.length > 0) for (const kid of d$4().getLineageFacts().childrenByParent.get(stack.pop()) ?? []) if (!doomed.has(kid.sessionId)) {
35577
- doomed.add(kid.sessionId);
35578
- stack.push(kid.sessionId);
35579
- }
35580
- }
35581
- const doomedPanelIds = new SvelteSet();
35582
- if (opts.family !== false && rootSessionId !== null) {
35583
- for (const p of st.panels) if (p.kind === "browser" && p.openedBy !== void 0 && doomed.has(p.openedBy)) doomedPanelIds.add(p.id);
35584
- }
35585
- if (opts.family !== false && root.kind === "workspace-explorer") {
35586
- for (const p of st.panels) if (p.kind === "workspace-file" && (p.explorerPanelId === root.id || p.explorerPanelId === null && p.sessionId === root.sessionId)) doomedPanelIds.add(p.id);
35587
- }
35588
- st.panels = st.panels.filter((p) => p.id !== root.id && !doomedPanelIds.has(p.id) && (p.kind === "conversation" ? !doomed.has(p.sessionId) : !("sessionId" in p) || p.sessionId === null || opts.family !== false && !doomed.has(p.sessionId)));
35589
- if (st.selectedPanelId !== null && !st.panels.some((p) => p.id === st.selectedPanelId)) st.selectedPanelId = st.panels[Math.min(idx, st.panels.length - 1)]?.id ?? null;
35590
- clearLoupeIfGone();
35591
- }
35592
36166
  //#endregion
35593
- //#region src/lib/services/panels/floor-registry/drag.svelte.ts
35594
- var d$2 = floorSeam;
35595
- /** Honest-slider's "slider sets all panels" leg (REALTIME, 2026-08-27):
35596
- * one width for every CONVERSATION column + the uniform preset.
35597
- * 2026-09-13: the workspace kinds are FIXED lanes — the explorer and
35598
- * file panels keep their own widths. */
35599
- function resizeAll(width) {
35600
- const w = clampPanelWidth(width);
35601
- d$2().setPanelWidthPreset(w);
35602
- st.panels = st.panels.map((p) => p.kind === "conversation" ? {
35603
- ...p,
35604
- width: w
35605
- } : p);
35606
- }
35607
- /** A transcript file link publishes ONLY after the tree route proves
35608
- * the file exists inside the session's workspace — the gate lives in
35609
- * the shared util; anything else drops the click. No navigation ever
35610
- * happens. No explorer yet ⇒ OPEN ONE (amendment 2026-09-25): the link
35611
- * itself opens the panel; without a workspace root there is nothing to
35612
- * open. W2 (T2.3): moved wholesale from the route — pure machine over
35613
- * the deps seam + the shared utils. */
35614
- async function openFileLinkFromMarkdown(sessionId, rawHref) {
35615
- const { path, lines } = parseFileLinkHref(rawHref);
35616
- if (path === "") return;
35617
- const target = await resolveFileLinkTarget(sessionId, path, d$2().getWorkspaceFor(sessionId), void 0, workspaceRootDirs);
35618
- if (target === null) return;
35619
- if (!sessionWorkspaceExplorer(sessionId)) {
35620
- const root = d$2().getWorkspaceFor(sessionId);
35621
- if (!root) return;
35622
- openWorkspaceExplorer(sessionId, root);
36167
+ //#region src/lib/services/conversation/spine-feed.svelte.ts
36168
+ var spineFeed = {
36169
+ rows: [],
36170
+ presets: [],
36171
+ a2aRows: [],
36172
+ workspaces: []
36173
+ };
36174
+ //#endregion
36175
+ //#region src/lib/services/workspace/page-feeds.svelte.ts
36176
+ var PageFeeds = class {
36177
+ coldCache = {};
36178
+ coldInFlight = new SvelteSet();
36179
+ deadSessions = {};
36180
+ edgeCache = {};
36181
+ /** Seed-arrival prime: the seed panel mounts with full cold props on
36182
+ * paint one (SSR parity for the seed path). Load-time constant. */
36183
+ primeSeed(sessionId, cold) {
36184
+ this.coldCache = {
36185
+ ...this.coldCache,
36186
+ [sessionId]: cold
36187
+ };
35623
36188
  }
35624
- openWorkspaceFile(sessionId, target, lines);
35625
- }
35626
- /** /events?full=1 carries entries+lastSeq+running+pending/settled
35627
- * answers (the ledger-tail resync surface); /history carries hasMore.
35628
- * Its beforeSeq anchor needs the tail's lastSeq, so the events response
35629
- * resolves first — the only serialization. W2 (T2.6): moved wholesale
35630
- * from the route — a pure wire read over the global fetch. */
35631
- async function fetchColdFromWire(sessionId) {
35632
- const eventsRes = await fetch(`/api/dsh/session/${encodeURIComponent(sessionId)}/events?full=1`);
35633
- if (!eventsRes.ok) return null;
35634
- const events = await eventsRes.json();
35635
- if (!events.ok) return null;
35636
- const lastSeq = events.lastSeq ?? -1;
35637
- let hasMore = false;
35638
- const historyRes = await fetch(`/api/dsh/session/${encodeURIComponent(sessionId)}/history?beforeSeq=${lastSeq + 1}`);
35639
- if (historyRes.ok) {
35640
- const history = await historyRes.json();
35641
- if (history.ok) hasMore = (history.entries?.length ?? 0) > 0 || history.hasMore === true;
36189
+ /** Record an ADD-time parent edge (the route's doAdd fork path). */
36190
+ rememberEdge(childSessionId, parentSessionId) {
36191
+ this.edgeCache = {
36192
+ ...this.edgeCache,
36193
+ [childSessionId]: parentSessionId
36194
+ };
35642
36195
  }
35643
- return {
35644
- title: null,
35645
- workspace: null,
35646
- agentPreset: null,
35647
- entries: events.entries ?? [],
35648
- lastSeq,
35649
- running: events.running ?? false,
35650
- hasMore,
35651
- pendingAnswers: events.pendingAnswers ?? [],
35652
- settledAnswers: events.settledAnswers ?? [],
35653
- permission: events.permission ?? null,
35654
- imageLimits: events.imageLimits ?? null,
35655
- knobEvents: events.knobEvents ?? [],
35656
- deliverables: events.deliverables ?? []
35657
- };
35658
- }
35659
- //#endregion
35660
- //#region src/lib/services/panels/floor-registry/factories.ts
35661
- /**
35662
- * floor-registry/factories — panel factories + placement slots (ADR-0022 W2).
35663
- *
35664
- * Moved WHOLE from the state home (floor-registry.svelte.ts): the eight
35665
- * make* panel factories and the slot grammar (insertPanel, insertionSlot,
35666
- * pinnedSlotIndex). Plain module: no runes here — every write goes through
35667
- * the state home's reactive `st` hub and its primitives (ADR-0019 D2:
35668
- * exactly ONE writer home for floor state). Sibling rule: this file never
35669
- * imports another sibling; shared needs come from the state home.
35670
- */
35671
- var d$1 = floorSeam;
35672
- function makePanel(sessionId, agentPreset) {
35673
- return {
35674
- id: nextPanelId(),
35675
- kind: "conversation",
35676
- sessionId,
35677
- agentPreset,
35678
- width: clampPanelWidth(d$1().getPanelWidth())
35679
- };
35680
- }
35681
- /** width: the reload-restored panel re-mints at the width the operator
35682
- * dragged before (browser-slot persistence, RCA 2026-10-22); absent for
35683
- * chip clicks and legacy slots — the honest-slider preset.
35684
- * openedBy: the opener session's provenance, recorded ONCE at spawn and
35685
- * never rewritten (ADR-0036 D1/D2); undefined = no conversation context. */
35686
- function makeBrowserPanel(width, openedBy, sessionId) {
35687
- return {
35688
- id: nextPanelId(),
35689
- kind: "browser",
35690
- width: clampPanelWidth(width ?? d$1().getPanelWidth()),
35691
- ...openedBy !== void 0 ? { openedBy } : {},
35692
- ...sessionId !== void 0 ? { browserSessionId: sessionId } : {}
35693
- };
35694
- }
35695
- function makeWorkspaceExplorerPanel(sessionId, root) {
35696
- return {
35697
- id: nextPanelId(),
35698
- kind: "workspace-explorer",
35699
- sessionId,
35700
- root,
35701
- expanded: [],
35702
- width: clampPanelWidth(850, "workspace-explorer")
35703
- };
35704
- }
35705
- function insertPanel(entry, index) {
35706
- const next = [...st.panels];
35707
- next.splice(Math.min(index, next.length), 0, entry);
35708
- st.panels = next;
35709
- st.selectedPanelId = entry.id;
35710
- }
35711
- function insertionSlot() {
35712
- return focusedInsertionSlot(st.panels, selectedIndex_(), d$1().getMoveFacts().parentOf);
35713
- }
35714
- //#endregion
35715
- //#region src/lib/services/panels/floor-registry.svelte.ts
35716
- var deps = null;
35717
- function configureFloorRegistry(d0) {
35718
- deps = d0;
35719
- }
35720
- function d() {
35721
- if (deps === null) throw new Error("floor-registry: configureFloorRegistry() was never called");
35722
- return deps;
35723
- }
35724
- /** Sibling-module access to the dep seam (ADR-0022 W2): the ONE outside-reads
35725
- * channel; siblings never open their own config channel. */
35726
- function floorSeam() {
35727
- return d();
35728
- }
35729
- var st = {
35730
- panels: [],
35731
- selectedPanelId: null,
35732
- loupePanelId: null,
35733
- focusComposerId: null,
35734
- panelIdSeq: 0,
35735
- terminalDeskMirrors: {},
35736
- pendingFileIntents: {},
35737
- fileIntentSeq: 0,
35738
- drag: null,
35739
- widthBadgeFading: false
35740
- };
35741
- /** Non-reactive gesture refs (drag start index, fade timer, badge ids) —
35742
- * written by floor-registry/drag.svelte.ts; the ids set is a live
35743
- * SvelteSet so badge visibility stays reactive. */
35744
- var dragRefs = {
35745
- ids: new SvelteSet()
35746
- };
35747
- var floor = {
35748
- get panels() {
35749
- return st.panels;
35750
- },
35751
- get selectedPanelId() {
35752
- return st.selectedPanelId;
35753
- },
35754
- get loupePanelId() {
35755
- return st.loupePanelId;
35756
- },
35757
- get focusComposerId() {
35758
- return st.focusComposerId;
35759
- },
35760
- get terminalDeskMirrors() {
35761
- return st.terminalDeskMirrors;
35762
- },
35763
- get pendingFileIntents() {
35764
- return st.pendingFileIntents;
35765
- },
35766
- get drag() {
35767
- return st.drag;
35768
- },
35769
- get widthBadgeFading() {
35770
- return st.widthBadgeFading;
35771
- },
35772
- get widthBadgeIds() {
35773
- return dragRefs.ids;
35774
- },
35775
- get selected() {
35776
- return st.panels.find((p) => p.id === st.selectedPanelId) ?? null;
35777
- },
35778
- get loupePanel() {
35779
- return st.panels.find((p) => p.id === st.loupePanelId) ?? null;
36196
+ /** One dual fetch, registries kept honest (in-flight guard, dead
36197
+ * session card, loader clear in `finally`). The fetcher is injected:
36198
+ * the wire call stays the route's (`fetchCold` reads the host
36199
+ * config); the bookkeeping lives here. */
36200
+ async runCold(sessionId, fetchCold) {
36201
+ if (this.coldCache[sessionId] !== void 0 || this.coldInFlight.has(sessionId)) return;
36202
+ this.coldInFlight = new SvelteSet(this.coldInFlight).add(sessionId);
36203
+ try {
36204
+ const cold = await fetchCold(sessionId);
36205
+ if (cold !== null) this.coldCache = {
36206
+ ...this.coldCache,
36207
+ [sessionId]: cold
36208
+ };
36209
+ else this.deadSessions = {
36210
+ ...this.deadSessions,
36211
+ [sessionId]: `Session ${sessionId} not found on the DSH host. It may have been reaped.`
36212
+ };
36213
+ } finally {
36214
+ const next = new SvelteSet(this.coldInFlight);
36215
+ next.delete(sessionId);
36216
+ this.coldInFlight = next;
36217
+ }
35780
36218
  }
35781
- };
35782
- function nextPanelId() {
35783
- st.panelIdSeq += 1;
35784
- return "panel-" + st.panelIdSeq;
35785
- }
35786
- function syncPanelIdSeq(entries) {
35787
- for (const p of entries) {
35788
- const m = /^panel-(\d+)$/.exec(p.id);
35789
- if (m) st.panelIdSeq = Math.max(st.panelIdSeq, Number(m[1]));
36219
+ /** The floor's two lifecycle feeds (call during component init —
36220
+ * `$effect` needs an owner):
36221
+ * - Prompt Sync hygiene (ADR "The Prompt Sync" D1/D6): a session
36222
+ * that left the floor holds no check and no snapshot; count 0
36223
+ * clears the broadcast. Conversation sessions only (D6).
36224
+ * - Spine feed lifecycle (W8): ref-counted single timer started on
36225
+ * mount, stopped on destroy; the a2a window rides the floor's
36226
+ * conversation senders (first sender per tick, never per panel).
36227
+ * Returns the spine subscription's cleanup. */
36228
+ installFeeds(getConversationSessionIds) {
36229
+ return () => {};
35790
36230
  }
35791
- }
35792
- function initFloor(entries, selectedId) {
35793
- st.panels = entries;
35794
- st.selectedPanelId = selectedId;
35795
- syncPanelIdSeq(entries);
35796
- }
35797
- /** The fresh panel mounted and its composer took focus — clear the
35798
- * swap-focus marker (a marker whose panel never mounts stays inert:
35799
- * panel ids are unique and never remount). */
35800
- function clearFocusComposer() {
35801
- st.focusComposerId = null;
35802
- }
35803
- function selectPanel(panelId) {
35804
- if (st.panels.some((p) => p.id === panelId)) st.selectedPanelId = panelId;
35805
- }
35806
- function closeLoupe() {
35807
- st.loupePanelId = null;
35808
- }
35809
- function clearLoupeIfGone() {
35810
- if (st.loupePanelId !== null && !st.panels.some((p) => p.id === st.loupePanelId)) st.loupePanelId = null;
35811
- }
35812
- function selectedIndex_() {
35813
- return st.panels.findIndex((p) => p.id === st.selectedPanelId);
35814
- }
36231
+ };
36232
+ var pageFeeds = new PageFeeds();
35815
36233
  //#endregion
35816
36234
  //#region src/lib/services/panels/glass-release.ts
35817
36235
  /**
@@ -36335,4 +36753,4 @@ function _page($$renderer, $$props) {
36335
36753
  }
36336
36754
 
36337
36755
  export { _page as default };
36338
- //# sourceMappingURL=_page.svelte.js-Dek8Q1_v.js.map
36756
+ //# sourceMappingURL=_page.svelte.js-CY7zcWr4.js.map