@deepseek-insight/dsi 0.6.12 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/dsi-thin/SKILL.md +140 -0
- package/.agents/skills/dsi-thin/references/ADR-0019-snapshot.md +363 -0
- package/.agents/skills/dsi-thin/references/authority-adr.md +28 -0
- package/.agents/skills/dsi-thin/references/input-contract.md +42 -0
- package/.agents/skills/dsi-thin/references/proven-gates.md +61 -0
- package/.agents/skills/dsi-thin/references/two-lane-contract.md +53 -0
- package/build/client/_app/immutable/assets/0.B-dIufDc.css +2 -0
- package/build/client/_app/immutable/assets/2.B70dfiy3.css +1 -0
- package/build/client/_app/immutable/chunks/{B9fOkvHb.js → 0HT0ILkA.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dwt_JXeR.js → 2KTqoQH-.js} +1 -1
- package/build/client/_app/immutable/chunks/{BIvFR1xA.js → 2M1KS72j.js} +1 -1
- package/build/client/_app/immutable/chunks/{a1Hqq5w-.js → 2XtVrxH4.js} +1 -1
- package/build/client/_app/immutable/chunks/38H6DLwz.js +1 -0
- package/build/client/_app/immutable/chunks/{Cdn_bzx2.js → 3E8jbhSl.js} +1 -1
- package/build/client/_app/immutable/chunks/{CmNEu9LM.js → A5Iiq81h.js} +1 -1
- package/build/client/_app/immutable/chunks/{C48u-2qp.js → ANnPTMPF.js} +1 -1
- package/build/client/_app/immutable/chunks/{BfChGCnE.js → B5S0Wke9.js} +1 -1
- package/build/client/_app/immutable/chunks/BBDOLPkB.js +1 -0
- package/build/client/_app/immutable/chunks/{DJ36HFmi.js → BImoJlJo.js} +1 -1
- package/build/client/_app/immutable/chunks/{DqRXQwgi.js → BJsp4NMH.js} +1 -1
- package/build/client/_app/immutable/chunks/{C7a8i7sD.js → BKU5-8Td.js} +1 -1
- package/build/client/_app/immutable/chunks/BMgyB-Sy.js +1 -0
- package/build/client/_app/immutable/chunks/BYHxHjHc.js +1 -0
- package/build/client/_app/immutable/chunks/{BC5nqwvj.js → BZ6uvzU8.js} +1 -1
- package/build/client/_app/immutable/chunks/{CvjdFV1E.js → B_Gknb5_.js} +1 -1
- package/build/client/_app/immutable/chunks/{CDTTZjYW.js → Bf1I5ROF.js} +1 -1
- package/build/client/_app/immutable/chunks/{CaAId7nh.js → Bh_Z0-Fh.js} +1 -1
- package/build/client/_app/immutable/chunks/{CLvIgAhR.js → BjafRUyA.js} +1 -1
- package/build/client/_app/immutable/chunks/{CG77uzLI.js → Bq4mG8qS.js} +1 -1
- package/build/client/_app/immutable/chunks/{CI1tkkzb.js → BqX9BB0G.js} +1 -1
- package/build/client/_app/immutable/chunks/BwNsQhd8.js +1 -0
- package/build/client/_app/immutable/chunks/{CvWnUnS1.js → Bx68-tOf.js} +1 -1
- package/build/client/_app/immutable/chunks/{RIIj2qfU.js → C1dC47iq.js} +1 -1
- package/build/client/_app/immutable/chunks/{BNx4_m7U.js → C6zqVyWx.js} +1 -1
- package/build/client/_app/immutable/chunks/{GLtnCmfd.js → C8seIiU_.js} +1 -1
- package/build/client/_app/immutable/chunks/{B_6pr4k2.js → CCWXLdgd.js} +1 -1
- package/build/client/_app/immutable/chunks/{D5ecapQY.js → CHYEvS-6.js} +1 -1
- package/build/client/_app/immutable/chunks/{OVDjSnH6.js → CIH5Juz4.js} +1 -1
- package/build/client/_app/immutable/chunks/CMByLjfJ.js +1 -0
- package/build/client/_app/immutable/chunks/{CnAA0dO8.js → CNJYmM8n.js} +1 -1
- package/build/client/_app/immutable/chunks/CP4wchdA.js +2 -0
- package/build/client/_app/immutable/chunks/{B2hm1w7K.js → CQTGR8fW.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bp4vT1Ke.js → CW2uYEvb.js} +1 -1
- package/build/client/_app/immutable/chunks/{BPFR73BS.js → C_Gd0Wor.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dn4fSUjR.js → C_MwBDNt.js} +1 -1
- package/build/client/_app/immutable/chunks/{Dea0pTfN.js → Ch7rc-L7.js} +4 -4
- package/build/client/_app/immutable/chunks/{J3KpJGd_.js → ChC4viXw.js} +1 -1
- package/build/client/_app/immutable/chunks/{4pCoNSa4.js → CiLhSVIj.js} +1 -1
- package/build/client/_app/immutable/chunks/{C7iYNw8X.js → Cx8YXmgi.js} +1 -1
- package/build/client/_app/immutable/chunks/{BRNBFqQ_.js → D4I0Dqwh.js} +1 -1
- package/build/client/_app/immutable/chunks/D6mTYwDH.js +6 -0
- package/build/client/_app/immutable/chunks/{l5GiMY16.js → D7-uGicP.js} +1 -1
- package/build/client/_app/immutable/chunks/{C8wGq5T2.js → D7XrPGMK.js} +1 -1
- package/build/client/_app/immutable/chunks/{xRO38jd8.js → D7eKc9ur.js} +1 -1
- package/build/client/_app/immutable/chunks/{CEj_BGCY.js → DCmONIPx.js} +1 -1
- package/build/client/_app/immutable/chunks/DJWLLhNJ.js +1 -0
- package/build/client/_app/immutable/chunks/{qkxAgKjo.js → DNf96C-O.js} +1 -1
- package/build/client/_app/immutable/chunks/{D4Ogpdhb.js → DSqRqQNp.js} +1 -1
- package/build/client/_app/immutable/chunks/{D4xFb7_b.js → DT1b6JaK.js} +1 -1
- package/build/client/_app/immutable/chunks/{D3bC95qg.js → DWYSL4om.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bi5NvDds.js → DYfx-RRD.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bi25XSWW.js → DfIqw6WM.js} +1 -1
- package/build/client/_app/immutable/chunks/{CGux-rdo.js → Dj4_qcIH.js} +4 -4
- package/build/client/_app/immutable/chunks/{Cx__ttXF.js → Dk9aRp2m.js} +1 -1
- package/build/client/_app/immutable/chunks/{C-58cZAk.js → DmLHHlla.js} +3 -3
- package/build/client/_app/immutable/chunks/{GNZHLVjs.js → Do0VAo0_.js} +1 -1
- package/build/client/_app/immutable/chunks/{BFsDYOFz.js → DrVddB75.js} +1 -1
- package/build/client/_app/immutable/chunks/{CdFbOrCf.js → Dso_gKeU.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bl78Sc72.js → DykTZ90H.js} +1 -1
- package/build/client/_app/immutable/chunks/{DNlR6G0m.js → GdM4UPkS.js} +1 -1
- package/build/client/_app/immutable/chunks/{B52Khb6E.js → IBXFCASw.js} +1 -1
- package/build/client/_app/immutable/chunks/{BIUi504d.js → IoX9lmZK.js} +1 -1
- package/build/client/_app/immutable/chunks/{Bn64J6A-.js → bpkmZ_Dv.js} +1 -1
- package/build/client/_app/immutable/chunks/{iK_uzrhN.js → crRp5OG4.js} +1 -1
- package/build/client/_app/immutable/chunks/da2fibLv.js +8 -0
- package/build/client/_app/immutable/chunks/{BOKyldQm.js → dcghWWmq.js} +1 -1
- package/build/client/_app/immutable/chunks/{CItDRTlN.js → fJBuRi-A.js} +1 -1
- package/build/client/_app/immutable/chunks/{CRe9IZg0.js → gXyYVhfn.js} +1 -1
- package/build/client/_app/immutable/chunks/{C2vkjTiv.js → j2lJ25s0.js} +1 -1
- package/build/client/_app/immutable/chunks/{DypDva7F.js → l3etNtOh.js} +1 -1
- package/build/client/_app/immutable/chunks/{BGQXNvll.js → olUfRNXS.js} +1 -1
- package/build/client/_app/immutable/chunks/{D3W5wGWM.js → uEyn1Z7p.js} +1 -1
- package/build/client/_app/immutable/entry/app.CCuVGpQc.js +2 -0
- package/build/client/_app/immutable/entry/start.BtgtnpyB.js +1 -0
- package/build/client/_app/immutable/nodes/0.GHtnahSb.js +1 -0
- package/build/client/_app/immutable/nodes/1.CaQ_jAq8.js +1 -0
- package/build/client/_app/immutable/nodes/2.DxOLlTKc.js +337 -0
- package/build/client/_app/version.json +1 -1
- package/build/handler.js +7 -6
- package/build/index.js +7 -6
- package/build/server/chunks/chunks/{app-config.svelte.js-BW7TwbBy.js → app-config.svelte.js-D7wkLUkK.js} +2 -2
- package/build/server/chunks/chunks/{db.js-BylhmazO.js → db.js-DRhjT6xK.js} +2 -2
- package/build/server/chunks/chunks/{db2.js-Cm8FBINY.js → db2.js-DUta2B-n.js} +207 -222
- package/build/server/chunks/chunks/{dsh-connection.js-Lnv7PlHw.js → dsh-connection.js-DRtqPR_j.js} +959 -752
- package/build/server/chunks/chunks/{dsh-events.js-D9tSYE7f.js → dsh-events.js-B_E1BU9F.js} +141 -350
- package/build/server/chunks/chunks/{git-probe.js-D_H3YQbR.js → git-probe.js-Daf6r7Z9.js} +3 -3
- package/build/server/chunks/chunks/index-server.js-CV2lq-ha.js +147 -0
- package/build/server/chunks/chunks/{insight-config.js-XUD5BN0g.js → insight-config.js-CA-iT3lB.js} +5 -3
- package/build/server/chunks/chunks/{internal2.js-BcS47nI-.js → internal.js-BcS47nI-.js} +1 -1
- package/build/server/chunks/chunks/{internal.js-D_QJoxN-.js → internal2.js-DAPn3Vk8.js} +11 -153
- package/build/server/chunks/chunks/merge.js-yMwW440S.js +228 -0
- package/build/server/chunks/chunks/{server.js-11-pxAWV.js → server.js-DqHGsQU2.js} +2 -2
- package/build/server/chunks/chunks/{state.js-DbUk4Hrz.js → state.js-CsIZcr7q.js} +5 -4
- package/build/server/chunks/chunks/theme-prefs.svelte.js-8CaWz46g.js +109 -0
- package/build/server/chunks/chunks/{watcher.js-DU-MZGLU.js → watcher.js-CoR53lwR.js} +5 -5
- package/build/server/chunks/entries/endpoints/api/a2a/_id_/{_server.ts.js-DQenu1Rx.js → _server.ts.js-CpeC2QLb.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/a2a/{_server.ts.js-BvEDeAhP.js → _server.ts.js-BKlF4XGH.js} +7 -6
- package/build/server/chunks/entries/endpoints/api/a2a/register/{_server.ts.js-DLJ4sQBq.js → _server.ts.js-BpEhlidV.js} +7 -6
- package/build/server/chunks/entries/endpoints/api/config/{_server.ts.js-zogZnK_B.js → _server.ts.js-BG78X__J.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/directory/{_server.ts.js-BAfRDeE_.js → _server.ts.js-Dmh7f-hU.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/{_server.ts.js-N21TnqYh.js → _server.ts.js-BEci0ZAx.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/presets/{_server.ts.js-C5ckDTov.js → _server.ts.js-OXFzXBSp.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/{_server.ts.js-CEPz4kVZ.js → _server.ts.js-x44GKy4R.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/{_server.ts.js-CUB5TKnW.js → _server.ts.js-Da5ZqeJ7.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/{_server.ts.js-BfDAa0Vb.js → _server.ts.js-BACv9hW-.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/changes/{_server.ts.js-QiLS5yAj.js → _server.ts.js-BtiUS3iD.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/{_server.ts.js-C5kJrRx-.js → _server.ts.js-BOOyd1LI.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/{_server.ts.js-vydjdEkJ.js → _server.ts.js-D0GwQbdF.js} +5 -4
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/{_server.ts.js-y5sqH_UC.js → _server.ts.js-Bd0JYMlf.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/{_server.ts.js-BoYIpj-f.js → _server.ts.js-DqAV7OOv.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/{_server.ts.js-SchAEll8.js → _server.ts.js-Cz4P-68E.js} +5 -4
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/{_server.ts.js-B8bmQCwT.js → _server.ts.js-C4fXXBIM.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/path/{_server.ts.js-DxiKkvE-.js → _server.ts.js-_SIiC8sn.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/{_server.ts.js-BBexDstc.js → _server.ts.js-BiqNZiiN.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/{_server.ts.js-fd9StFXw.js → _server.ts.js-Bdx9V4oY.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/{_server.ts.js-98HaDusY.js → _server.ts.js-CuBqoV3c.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/{_server.ts.js-DN9APffn.js → _server.ts.js-CXPFI_Pn.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/{_server.ts.js-CeROEmUY.js → _server.ts.js-CRcaw4Yo.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/sessions/{_server.ts.js-DKHmiqIs.js → _server.ts.js-dYrtVHDP.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/{_server.ts.js-S6XZDJmC.js → _server.ts.js-BtlfuMjf.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/{_server.ts.js-CA4PUERZ.js → _server.ts.js-D12kAP9B.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/{_server.ts.js-BObM3Axz.js → _server.ts.js-C9JHp1WU.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/{_server.ts.js-CA34p4GV.js → _server.ts.js-bLF4Fbgn.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/{_server.ts.js-DPoRM_7u.js → _server.ts.js-BSw_zD-0.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/prompts/_id_/{_server.ts.js-u-regI3R.js → _server.ts.js-bRLZ5TWI.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/prompts/{_server.ts.js-YtkQGu7O.js → _server.ts.js-ChzWZoFk.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/prompts/use/{_server.ts.js-tv0ivn2x.js → _server.ts.js-DdH5QmPS.js} +3 -3
- package/build/server/chunks/entries/endpoints/api/settings/{_server.ts.js-DmU2MBc5.js → _server.ts.js-DwGQj520.js} +2 -2
- package/build/server/chunks/entries/endpoints/api/terminal/{_server.ts.js-DHhod6dT.js → _server.ts.js-CwXC6Stk.js} +3 -2
- package/build/server/chunks/entries/endpoints/api/workspace/file-write/{_server.ts.js-BmEUPTSa.js → _server.ts.js-CqyKA9g2.js} +6 -5
- package/build/server/chunks/entries/endpoints/api/workspace/git-events/{_server.ts.js-_1uYvYEA.js → _server.ts.js-yu695YoE.js} +6 -5
- package/build/server/chunks/entries/endpoints/api/workspace/git-file-head/{_server.ts.js-DaXO3et9.js → _server.ts.js-BhDAvM6y.js} +6 -5
- package/build/server/chunks/entries/endpoints/api/workspace/git-map/{_server.ts.js-hp85iKSu.js → _server.ts.js-BRUgPrJ2.js} +6 -5
- package/build/server/chunks/entries/endpoints/api/workspace/git-status/{_server.ts.js-CPB-9252.js → _server.ts.js-C7VJ2G4_.js} +6 -5
- package/build/server/chunks/entries/endpoints/api/workspace/write-gate/{_server.ts.js-DKqzSQBE.js → _server.ts.js-BH5BRA_d.js} +6 -5
- package/build/server/chunks/entries/pages/{_error.svelte.js-AsgAVyDR.js → _error.svelte.js-B5TEtccE.js} +10 -9
- package/build/server/chunks/entries/pages/_layout.svelte.js-DRfiLzlz.js +20 -0
- package/build/server/chunks/entries/pages/{_page.server.ts.js-DTjoTdYK.js → _page.server.ts.js-CityLNBO.js} +3 -3
- package/build/server/chunks/entries/pages/{_page.svelte.js-CFdCjELr.js → _page.svelte.js-i6KsWK9O.js} +4654 -2413
- package/build/server/chunks/{handler-Bx2fbkEJ.js → handler-XuK3Ihhf.js} +3 -3
- package/build/server/chunks/{index.js-BldXQ38P.js → index.js-D2FEiD0I.js} +5 -4
- package/build/server/chunks/{manifest.js-Du-9DaEd.js → manifest.js-B_kahcFI.js} +45 -45
- package/build/server/chunks/nodes/0.js-C0B2o0le.js +12 -0
- package/build/server/chunks/nodes/1.js-C2e7NFqd.js +9 -0
- package/build/server/chunks/nodes/{2.js-DGLcInR6.js → 2.js-Bh3kLeYq.js} +9 -8
- package/package.json +2 -2
- package/skills/dsi-sloc/SKILL.md +117 -0
- package/skills/dsi-sloc/references/measurement.md +79 -0
- package/skills/dsi-sloc/references/report-skeleton.md +79 -0
- package/skills/dsi-thin/SKILL.md +214 -0
- package/skills/dsi-thin/references/ADR-0019-snapshot.md +363 -0
- package/skills/dsi-thin/references/authority-adr.md +28 -0
- package/skills/dsi-thin/references/input-contract.md +42 -0
- package/skills/dsi-thin/references/proven-gates.md +61 -0
- package/skills/dsi-thin/references/two-lane-contract.md +53 -0
- package/template/.dsi/prompts.sqlite +0 -0
- package/template/.dsi/prompts.sqlite-shm +0 -0
- package/template/.dsi/prompts.sqlite-wal +0 -0
- package/template/.dsi/resources/skills-reff.md +5 -4
- package/build/client/_app/immutable/assets/0.CRU0GiZX.css +0 -2
- package/build/client/_app/immutable/assets/2.Bzy6Q5kl.css +0 -1
- package/build/client/_app/immutable/chunks/-BV4MEqr.js +0 -1
- package/build/client/_app/immutable/chunks/66qbK5h1.js +0 -2
- package/build/client/_app/immutable/chunks/BLxS3bAX.js +0 -6
- package/build/client/_app/immutable/chunks/CPhcxli7.js +0 -1
- package/build/client/_app/immutable/chunks/CT1m72HM.js +0 -1
- package/build/client/_app/immutable/chunks/CxnI-Htc.js +0 -8
- package/build/client/_app/immutable/chunks/D0uZseUH.js +0 -1
- package/build/client/_app/immutable/chunks/D73RufXg.js +0 -1
- package/build/client/_app/immutable/chunks/HwuqC1sa.js +0 -1
- package/build/client/_app/immutable/chunks/NtHUheoE.js +0 -1
- package/build/client/_app/immutable/entry/app.D8WfdLSe.js +0 -2
- package/build/client/_app/immutable/entry/start.CVl0g0cv.js +0 -1
- package/build/client/_app/immutable/nodes/0.k1Ikzbp6.js +0 -1
- package/build/client/_app/immutable/nodes/1.X0If5xAN.js +0 -1
- package/build/client/_app/immutable/nodes/2.CELzu2i6.js +0 -337
- package/build/server/chunks/entries/pages/_layout.svelte.js-EFiihTdF.js +0 -18
- package/build/server/chunks/nodes/0.js-B0560RI0.js +0 -12
- package/build/server/chunks/nodes/1.js-D55AmdZs.js +0 -9
package/build/server/chunks/chunks/{dsh-connection.js-Lnv7PlHw.js → dsh-connection.js-DRtqPR_j.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v as dshBaseUrl } from './config.js-B0w1-uCA.js';
|
|
2
|
-
import { r as
|
|
2
|
+
import { r as readDshAuthConfig, a as readServerConfig, b as readDshPresetEnglishConfig } from './insight-config.js-CA-iT3lB.js';
|
|
3
3
|
|
|
4
4
|
//#region src/lib/server/dsh-rpc.ts
|
|
5
5
|
/**
|
|
@@ -369,116 +369,436 @@ var DshAuth = class {
|
|
|
369
369
|
}
|
|
370
370
|
};
|
|
371
371
|
//#endregion
|
|
372
|
-
//#region src/lib/server/
|
|
372
|
+
//#region src/lib/server/connection/normalize.ts
|
|
373
|
+
/** The follow-<sessionId>-<streamSeq> logical stream id (one per generation). */
|
|
374
|
+
function followStreamId(sessionId, streamSeq) {
|
|
375
|
+
return `follow-${sessionId}-${String(streamSeq)}`;
|
|
376
|
+
}
|
|
373
377
|
/**
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
* downlink WebSocket paths).
|
|
377
|
-
*
|
|
378
|
-
* State model (PRD §3.5):
|
|
379
|
-
* mux frames (session/subscribed, session/event) → per-session ring buffer
|
|
380
|
-
* host frames (host/session-status) → per-session running map
|
|
381
|
-
* running map re-baselines from session.list at every generation open
|
|
382
|
-
* (2026-08-26): the host pushes status only on TRANSITIONS and sends no
|
|
383
|
-
* initial snapshot, so a downlink rebuilt mid-turn would otherwise never
|
|
384
|
-
* learn the session was already running.
|
|
385
|
-
* ring buffer = freshness only; the ledger (session.history) is the truth —
|
|
386
|
-
* cold loads and every resync re-read the ledger (BC-4).
|
|
387
|
-
*
|
|
388
|
-
* Generation semantics (guide Part 1): the two downlinks form one generation;
|
|
389
|
-
* if either dies, both are rebuilt after backoff.
|
|
378
|
+
* Expand ONE follow/page event payload into its ring-buffer form
|
|
379
|
+
* (single-element list — ledger v2 serves every record as a plain event).
|
|
390
380
|
*/
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
381
|
+
function expandEntry(e) {
|
|
382
|
+
return [{
|
|
383
|
+
type: e.type,
|
|
384
|
+
seq: typeof e.seq === "number" ? e.seq : -1,
|
|
385
|
+
time: typeof e.time === "number" ? e.time : 0,
|
|
386
|
+
data: e.data
|
|
387
|
+
}];
|
|
395
388
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
function
|
|
399
|
-
|
|
400
|
-
return
|
|
389
|
+
/** Expand one page/snapshot record ({type:'event', event}) into its member
|
|
390
|
+
* events; a record without an event payload contributes nothing. */
|
|
391
|
+
function expandRecord(record) {
|
|
392
|
+
if (record === void 0 || record.event === void 0) return [];
|
|
393
|
+
return expandEntry(record.event);
|
|
401
394
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
395
|
+
/** Expand a records array into a flat event list. */
|
|
396
|
+
function expandRecords(records) {
|
|
397
|
+
return records.flatMap((record) => expandRecord(record));
|
|
398
|
+
}
|
|
399
|
+
/** The last member time of one compact stream record, or undefined when
|
|
400
|
+
* the record carries no readable time (malformed members stay silent —
|
|
401
|
+
* the baseline is presentation seed data, never re-validated truth). */
|
|
402
|
+
function compactRecordTailTime(record) {
|
|
403
|
+
if (record.type === "chunk") return typeof record.time === "number" ? record.time : void 0;
|
|
404
|
+
if (record.type === "text-chunks" || record.type === "reasoning-chunks" || record.type === "tool-call-chunks") {
|
|
405
|
+
const time0 = typeof record.time0 === "number" ? record.time0 : void 0;
|
|
406
|
+
const dt = Array.isArray(record.dt) ? record.dt : [];
|
|
407
|
+
if (time0 === void 0 || dt.some((g) => typeof g !== "number")) return void 0;
|
|
408
|
+
return dt.reduce((t, g) => t + g, time0);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function normalizeModelSelection(v) {
|
|
412
|
+
if (v === null || typeof v !== "object") return null;
|
|
413
|
+
const sel = v;
|
|
414
|
+
if (typeof sel.provider !== "string" || typeof sel.model !== "string") return null;
|
|
415
|
+
return {
|
|
416
|
+
provider: sel.provider,
|
|
417
|
+
model: sel.model,
|
|
418
|
+
...typeof sel.reasoningEffort === "string" ? { reasoningEffort: sel.reasoningEffort } : {}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Conservative normalize of a SessionModels value: structural reads only,
|
|
423
|
+
* never throws, never invents data (task 3.2-T: a garbage catalog → empty
|
|
424
|
+
* groups + empty failures, honest `routable`/`current` — the selector can
|
|
425
|
+
* still show the current selection and say nothing is listed). Reasoning
|
|
426
|
+
* efforts are flattened to opaque ids (the adapter owns their meaning).
|
|
427
|
+
*/
|
|
428
|
+
function normalizeModelDirectory(raw) {
|
|
429
|
+
const empty = {
|
|
430
|
+
current: null,
|
|
431
|
+
routable: false,
|
|
432
|
+
groups: [],
|
|
433
|
+
failures: []
|
|
434
|
+
};
|
|
435
|
+
if (raw === null || typeof raw !== "object") return empty;
|
|
436
|
+
const dir = raw;
|
|
437
|
+
const groups = Array.isArray(dir.groups) ? modelGroupsOf(dir.groups) : [];
|
|
438
|
+
const failures = Array.isArray(dir.failures) ? modelFailuresOf(dir.failures) : [];
|
|
439
|
+
if (dir.current === void 0 && dir.default !== void 0) return {
|
|
440
|
+
current: normalizeModelSelection(dir.default),
|
|
441
|
+
routable: Array.isArray(dir.routableProviders) ? dir.routableProviders.length > 0 || groups.length > 0 : true,
|
|
442
|
+
groups,
|
|
443
|
+
failures
|
|
444
|
+
};
|
|
445
|
+
return {
|
|
446
|
+
current: normalizeModelSelection(dir.current),
|
|
447
|
+
routable: dir.routable === true,
|
|
448
|
+
groups,
|
|
449
|
+
failures
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
/** ModelCatalog provider groups → DsiModelGroup rows (conservative). */
|
|
453
|
+
function modelGroupsOf(groups) {
|
|
454
|
+
return groups.flatMap((g) => {
|
|
455
|
+
if (g === null || typeof g !== "object") return [];
|
|
456
|
+
const grp = g;
|
|
457
|
+
if (typeof grp.id !== "string" || grp.id.length === 0) return [];
|
|
458
|
+
const models = Array.isArray(grp.models) ? grp.models.flatMap((m) => {
|
|
459
|
+
if (m === null || typeof m !== "object") return [];
|
|
460
|
+
const mod = m;
|
|
461
|
+
if (typeof mod.id !== "string" || mod.id.length === 0) return [];
|
|
462
|
+
return [{
|
|
463
|
+
id: mod.id,
|
|
464
|
+
name: typeof mod.name === "string" ? mod.name : null,
|
|
465
|
+
...Array.isArray(mod.reasoning?.efforts) ? { reasoningEfforts: mod.reasoning.efforts.flatMap((e) => typeof e?.id === "string" ? [e.id] : []) } : {}
|
|
466
|
+
}];
|
|
467
|
+
}) : [];
|
|
468
|
+
return [{
|
|
469
|
+
id: grp.id,
|
|
470
|
+
name: typeof grp.name === "string" ? grp.name : null,
|
|
471
|
+
models
|
|
472
|
+
}];
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
/** ModelCatalog provider failures → diagnostic rows (conservative). */
|
|
476
|
+
function modelFailuresOf(failures) {
|
|
477
|
+
return failures.flatMap((f) => {
|
|
478
|
+
if (f === null || typeof f !== "object") return [];
|
|
479
|
+
const fail = f;
|
|
480
|
+
if (typeof fail.id !== "string" || fail.id.length === 0) return [];
|
|
481
|
+
return [{
|
|
482
|
+
id: fail.id,
|
|
483
|
+
name: typeof fail.name === "string" ? fail.name : null,
|
|
484
|
+
message: typeof fail.message === "string" ? fail.message : null
|
|
485
|
+
}];
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function normalizeSessionRow(row) {
|
|
489
|
+
return {
|
|
490
|
+
sessionId: row.sessionId,
|
|
491
|
+
title: row.origin === "subagent" ? readSubagentLabel(row) ?? readTitle(row) : readTitle(row),
|
|
492
|
+
agentPreset: readAgentPreset(row) ?? row.agentPreset ?? null,
|
|
493
|
+
running: Boolean(row.running),
|
|
494
|
+
blank: Boolean(row.blank),
|
|
495
|
+
updatedAt: row.updatedAt ?? 0,
|
|
496
|
+
workspace: typeof row.cwd === "string" && row.cwd.length > 0 ? row.cwd : null,
|
|
497
|
+
turns: readTurns(row),
|
|
498
|
+
parentSessionId: row.parentSessionId ?? null,
|
|
499
|
+
origin: row.origin ?? null
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function readTitle(row) {
|
|
503
|
+
const title = row.projections?.values?.["title"];
|
|
504
|
+
return typeof title === "string" && title.length > 0 ? title : null;
|
|
505
|
+
}
|
|
506
|
+
/** projections.values.subagent.label (the child descriptor's creation label;
|
|
507
|
+
* one-shot children may omit it — structural read, null when unreadable). */
|
|
508
|
+
function readSubagentLabel(row) {
|
|
509
|
+
const identity = row.projections?.values?.["subagent"];
|
|
510
|
+
const label = identity !== null && typeof identity === "object" ? identity.label : void 0;
|
|
511
|
+
return typeof label === "string" && label.length > 0 ? label : null;
|
|
512
|
+
}
|
|
513
|
+
/** projections.values.agentPreset (0.1.2 home of the preset id on list rows). */
|
|
514
|
+
function readAgentPreset(row) {
|
|
515
|
+
const preset = row.projections?.values?.["agentPreset"];
|
|
516
|
+
return typeof preset === "string" && preset.length > 0 ? preset : null;
|
|
517
|
+
}
|
|
518
|
+
/** `sessionStats.turns` (0.1.2: projections.values.sessionStats) — integer-strict, null on absent/junk. */
|
|
519
|
+
function readTurns(row) {
|
|
520
|
+
const stats = row.projections?.values?.["sessionStats"];
|
|
521
|
+
const turns = stats !== null && typeof stats === "object" ? stats.turns : row.sessionStats?.turns;
|
|
522
|
+
if (typeof turns !== "number" || !Number.isInteger(turns) || turns < 0) return null;
|
|
523
|
+
return turns;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* English display copy for the host's SHIPPED presets, keyed by the stable
|
|
527
|
+
* preset id. The wire's `name`/`description` are the UNLOCALIZED metadata a
|
|
528
|
+
* preset publishes (DSH `PresetDisplaySource`), and the four built-ins ship
|
|
529
|
+
* Simplified Chinese there — DSH localizes them only inside its own web UI
|
|
530
|
+
* (`packages/preset/agent-presets/src/display.ts` folds trust:'system' + id →
|
|
531
|
+
* locale dictionary keys). This app has no host locale seat, so the overlay
|
|
532
|
+
* mirrors that fold in English: trust:'system' + a known id → the English
|
|
533
|
+
* copy; every other row (user presets, unknown future ids) keeps the host's
|
|
534
|
+
* name verbatim — user metadata is never translated, and an unknown id falls
|
|
535
|
+
* back honestly. Operator-tunable off via ~/.dsi/settings.yaml
|
|
536
|
+
* `dsh.presetEnglish: false` (readDshPresetEnglishConfig — no cache, so the
|
|
537
|
+
* next listPresets fetch picks an edit up). Keep the strings in step with DSH
|
|
538
|
+
* `packages/client/ui-agent-preset/src/client/locales.ts` (the `en` bundle).
|
|
539
|
+
*/
|
|
540
|
+
var SHIPPED_PRESET_ENGLISH = {
|
|
541
|
+
standard: {
|
|
542
|
+
name: "Standard mode",
|
|
543
|
+
description: "Full coding agent with file editing, shell, file and web search, skills, planning, goals, subagents, and workflows."
|
|
544
|
+
},
|
|
545
|
+
ptc: {
|
|
546
|
+
name: "PTC mode",
|
|
547
|
+
description: "Full coding agent without the workflow tool; other tools are exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program."
|
|
548
|
+
},
|
|
549
|
+
minimal: {
|
|
550
|
+
name: "Minimal mode",
|
|
551
|
+
description: "Two-tool coding agent with persistent bash and str_replace_editor."
|
|
552
|
+
},
|
|
553
|
+
cordis: {
|
|
554
|
+
name: "Creator mode",
|
|
555
|
+
description: "Built for creating custom agent presets, with all Standard mode capabilities plus runtime inspection, plugin experiments, and preset-authoring guidance."
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* Conservative normalize: unknown shapes keep their id but never invent data.
|
|
560
|
+
* The one English overlay seat (SHIPPED_PRESET_ENGLISH) applies before the
|
|
561
|
+
* host fallbacks — a shipped preset's label is locale-resolved here, never
|
|
562
|
+
* the wire's unlocalized text. Operator-tunable via
|
|
563
|
+
* ~/.dsi/settings.yaml `dsh.presetEnglish` (read live per listPresets
|
|
564
|
+
* call, passed through here): false hands the wire name through untouched.
|
|
565
|
+
*/
|
|
566
|
+
function normalizePreset(row, overlayEnglish = true) {
|
|
567
|
+
const id = typeof row.id === "string" ? row.id : String(row.id ?? "");
|
|
568
|
+
const shipped = overlayEnglish && row.trust === "system" ? SHIPPED_PRESET_ENGLISH[id] : void 0;
|
|
569
|
+
return {
|
|
570
|
+
id,
|
|
571
|
+
name: shipped?.name ?? (typeof row.name === "string" ? row.name : null),
|
|
572
|
+
description: shipped?.description ?? (typeof row.description === "string" ? row.description : null),
|
|
573
|
+
isDefault: row.isDefault === true
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
577
|
+
//#region src/lib/server/connection/session-state.ts
|
|
578
|
+
var SessionStateStore = class {
|
|
579
|
+
deps;
|
|
409
580
|
log;
|
|
410
581
|
buffers = /* @__PURE__ */ new Map();
|
|
411
582
|
running = /* @__PURE__ */ new Map();
|
|
412
|
-
/**
|
|
413
|
-
* Live assistant-stream tails (0.1.3-alpha.1): the one in-flight model
|
|
414
|
-
* attempt per session, folded from the follow's seq-less assistant-stream
|
|
415
|
-
* frames. Presentation state only — never part of the durable cursor
|
|
416
|
-
* algebra (lastSeq/gap/coverage), because the frames carry no seq.
|
|
417
|
-
*/
|
|
418
583
|
liveTails = /* @__PURE__ */ new Map();
|
|
419
|
-
/**
|
|
420
|
-
* Session-ids that received a host/session-status push since the running
|
|
421
|
-
* re-baseline RPC was ISSUED (2026-08-26). A push is a strictly newer
|
|
422
|
-
* transition than an in-flight snapshot — the seed must never overwrite
|
|
423
|
-
* one. Swapped for a fresh set every time a seed is issued; read at
|
|
424
|
-
* apply time so pushes landing mid-flight are counted.
|
|
425
|
-
*/
|
|
426
584
|
runningSeedRaces = /* @__PURE__ */ new Set();
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
585
|
+
constructor(deps) {
|
|
586
|
+
this.deps = deps;
|
|
587
|
+
this.log = deps.log;
|
|
588
|
+
}
|
|
589
|
+
setRunning(sessionId, running) {
|
|
590
|
+
this.running.set(sessionId, running);
|
|
591
|
+
}
|
|
592
|
+
bufferKeys() {
|
|
593
|
+
return this.buffers.keys();
|
|
594
|
+
}
|
|
595
|
+
dispose() {
|
|
596
|
+
this.buffers.clear();
|
|
597
|
+
this.liveTails.clear();
|
|
598
|
+
this.running.clear();
|
|
599
|
+
this.runningSeedRaces.clear();
|
|
600
|
+
}
|
|
601
|
+
onHostFrame(payload) {
|
|
602
|
+
const frame = payload;
|
|
603
|
+
if (frame?.type === "host/session-status" && typeof frame.sessionId === "string") {
|
|
604
|
+
this.runningSeedRaces.add(frame.sessionId);
|
|
605
|
+
this.running.set(frame.sessionId, Boolean(frame.running));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async seedRunning() {
|
|
609
|
+
const generation = this.deps.generation();
|
|
610
|
+
this.runningSeedRaces = /* @__PURE__ */ new Set();
|
|
611
|
+
try {
|
|
612
|
+
const { items } = await this.deps.listSessions();
|
|
613
|
+
if (this.deps.disposed() || generation !== this.deps.generation()) return;
|
|
614
|
+
const races = this.runningSeedRaces;
|
|
615
|
+
const next = /* @__PURE__ */ new Map();
|
|
616
|
+
for (const [id, running] of this.running) if (races.has(id)) next.set(id, running);
|
|
617
|
+
for (const row of items) if (!races.has(row.sessionId)) next.set(row.sessionId, row.running);
|
|
618
|
+
this.running = next;
|
|
619
|
+
this.log(`dsh-connection: running map re-baselined (${items.length} sessions)`);
|
|
620
|
+
} catch (err) {
|
|
621
|
+
this.log("dsh-connection: running re-baseline failed — pushes stay authoritative", err);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
bufferFor(sessionId) {
|
|
625
|
+
let buf = this.buffers.get(sessionId);
|
|
626
|
+
if (!buf) {
|
|
627
|
+
buf = {
|
|
628
|
+
entries: [],
|
|
629
|
+
lastSeq: -1,
|
|
630
|
+
coverageFloor: -1
|
|
631
|
+
};
|
|
632
|
+
this.buffers.set(sessionId, buf);
|
|
633
|
+
}
|
|
634
|
+
return buf;
|
|
635
|
+
}
|
|
636
|
+
seedBaseline(sessionId, lastSeq) {
|
|
637
|
+
const buf = this.bufferFor(sessionId);
|
|
638
|
+
if (lastSeq > buf.lastSeq && buf.entries.length === 0) {
|
|
639
|
+
buf.lastSeq = lastSeq;
|
|
640
|
+
buf.coverageFloor = lastSeq;
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (buf.lastSeq >= 0 && lastSeq < buf.lastSeq) {
|
|
644
|
+
buf.entries.length = 0;
|
|
645
|
+
buf.lastSeq = lastSeq;
|
|
646
|
+
buf.coverageFloor = lastSeq;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
seedSnapshotCursor(sessionId, cursor) {
|
|
650
|
+
const buf = this.bufferFor(sessionId);
|
|
651
|
+
if (cursor > buf.lastSeq) {
|
|
652
|
+
buf.lastSeq = cursor;
|
|
653
|
+
buf.coverageFloor = cursor;
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (buf.lastSeq >= 0 && cursor < buf.lastSeq) {
|
|
657
|
+
buf.entries.length = 0;
|
|
658
|
+
buf.lastSeq = cursor;
|
|
659
|
+
buf.coverageFloor = cursor;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
appendEvent(sessionId, event) {
|
|
663
|
+
const buf = this.bufferFor(sessionId);
|
|
664
|
+
if (event.seq > buf.lastSeq) {
|
|
665
|
+
if (buf.lastSeq >= 0 && event.seq > buf.lastSeq + 1) {
|
|
666
|
+
buf.coverageFloor = Math.max(buf.coverageFloor, buf.lastSeq);
|
|
667
|
+
this.log(`dsh-connection: seq gap for ${sessionId} (${buf.lastSeq} → ${event.seq}) — resync required below ${event.seq}`);
|
|
668
|
+
}
|
|
669
|
+
buf.lastSeq = event.seq;
|
|
670
|
+
buf.entries.push(event);
|
|
671
|
+
const cap = this.deps.ringCapacity();
|
|
672
|
+
if (buf.entries.length > cap) buf.entries.splice(0, buf.entries.length - cap);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
onAssistantStreamFrame(sessionId, frame) {
|
|
676
|
+
if (frame.type === "start") {
|
|
677
|
+
this.liveTails.set(sessionId, {
|
|
678
|
+
attemptId: frame.attemptId,
|
|
679
|
+
turn: frame.turn,
|
|
680
|
+
step: frame.step,
|
|
681
|
+
startedAfterSeq: frame.startedAfterSeq,
|
|
682
|
+
startedAt: Date.now(),
|
|
683
|
+
lastTime: Date.now(),
|
|
684
|
+
text: "",
|
|
685
|
+
reasoning: "",
|
|
686
|
+
nextIndex: 0
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const tail = this.liveTails.get(sessionId);
|
|
691
|
+
if (tail === void 0) return;
|
|
692
|
+
if (frame.type === "chunk") {
|
|
693
|
+
if (frame.attemptId !== tail.attemptId || frame.index !== tail.nextIndex) {
|
|
694
|
+
this.liveTails.delete(sessionId);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const kind = frame.chunk?.type;
|
|
698
|
+
if (kind === "text-delta" && typeof frame.chunk?.text === "string") tail.text += frame.chunk.text;
|
|
699
|
+
else if (kind === "reasoning-delta" && typeof frame.chunk?.text === "string") tail.reasoning += frame.chunk.text;
|
|
700
|
+
tail.nextIndex += 1;
|
|
701
|
+
if (typeof frame.time === "number") tail.lastTime = frame.time;
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
this.liveTails.delete(sessionId);
|
|
705
|
+
}
|
|
706
|
+
seedLiveTailFromBaseline(sessionId, baseline) {
|
|
707
|
+
const active = baseline?.activeAttempt;
|
|
708
|
+
if (active === void 0) {
|
|
709
|
+
this.liveTails.delete(sessionId);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const tail = {
|
|
713
|
+
attemptId: active.attemptId,
|
|
714
|
+
turn: active.turn,
|
|
715
|
+
step: active.step,
|
|
716
|
+
startedAfterSeq: active.startedAfterSeq,
|
|
717
|
+
startedAt: Date.now(),
|
|
718
|
+
lastTime: Date.now(),
|
|
719
|
+
text: "",
|
|
720
|
+
reasoning: "",
|
|
721
|
+
nextIndex: active.nextIndex
|
|
722
|
+
};
|
|
723
|
+
for (const record of active.stream ?? []) {
|
|
724
|
+
if (record.type === "text-chunks" || record.type === "reasoning-chunks") {
|
|
725
|
+
const texts = record.texts;
|
|
726
|
+
const joined = Array.isArray(texts) ? texts.filter((t) => typeof t === "string").join("") : "";
|
|
727
|
+
if (record.type === "text-chunks") tail.text += joined;
|
|
728
|
+
else tail.reasoning += joined;
|
|
729
|
+
}
|
|
730
|
+
const times = compactRecordTailTime(record);
|
|
731
|
+
if (times !== void 0) tail.lastTime = times;
|
|
732
|
+
}
|
|
733
|
+
this.liveTails.set(sessionId, tail);
|
|
734
|
+
}
|
|
735
|
+
liveAssistantStream(sessionId) {
|
|
736
|
+
const tail = this.liveTails.get(sessionId);
|
|
737
|
+
if (tail === void 0) return null;
|
|
738
|
+
return {
|
|
739
|
+
id: `a:${String(tail.turn)}:${String(tail.step)}`,
|
|
740
|
+
turn: tail.turn,
|
|
741
|
+
step: tail.step,
|
|
742
|
+
seq: tail.startedAfterSeq,
|
|
743
|
+
time: tail.lastTime,
|
|
744
|
+
text: tail.text,
|
|
745
|
+
reasoning: tail.reasoning
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
eventsSince(sessionId, n) {
|
|
749
|
+
const buf = this.bufferFor(sessionId);
|
|
750
|
+
const events = buf.entries.filter((e) => e.seq > n);
|
|
751
|
+
const firstDelta = events[0]?.seq;
|
|
752
|
+
const gap = firstDelta !== void 0 && firstDelta > n + 1;
|
|
753
|
+
return {
|
|
754
|
+
events,
|
|
755
|
+
lastSeq: buf.lastSeq,
|
|
756
|
+
running: this.running.get(sessionId) ?? false,
|
|
757
|
+
gap
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
hasBaseline(sessionId) {
|
|
761
|
+
return this.buffers.has(sessionId) && this.bufferFor(sessionId).lastSeq >= 0;
|
|
762
|
+
}
|
|
763
|
+
isRunning(sessionId) {
|
|
764
|
+
return this.running.get(sessionId) ?? false;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
//#endregion
|
|
768
|
+
//#region src/lib/server/connection/downlinks.ts
|
|
769
|
+
/**
|
|
770
|
+
* connection/downlinks — the downlink generation lifecycle + frame demux
|
|
771
|
+
* (ADR-0022 W3, ADR-0019 move-whole): the ONE cookie-authenticated remote.mux
|
|
772
|
+
* carrier, its logical streams (session/follow, $events, workspace/follow,
|
|
773
|
+
* session/control), generation rebuild + backoff, the 0.1.2 frame demux,
|
|
774
|
+
* and the per-session projection / workspace-registry caches.
|
|
775
|
+
*/
|
|
776
|
+
var DownlinkManager = class {
|
|
777
|
+
conn;
|
|
778
|
+
constructor(conn) {
|
|
779
|
+
this.conn = conn;
|
|
780
|
+
}
|
|
781
|
+
wsMux;
|
|
782
|
+
generation = 0;
|
|
783
|
+
/** Logical streams by streamId → owning socket (0.1.2 remote.mux). */
|
|
784
|
+
streamSockets = /* @__PURE__ */ new Map();
|
|
785
|
+
/** Generation-scoped counter keeping stream ids unique across rebuilds. */
|
|
786
|
+
streamSeq = 0;
|
|
787
|
+
/** $events generation clientId (RemoteEventReadyFrame; binds $events/result). */
|
|
788
|
+
eventClientId = null;
|
|
789
|
+
/** True once the carrier's OPEN event fired (sends are legal). */
|
|
790
|
+
opened = false;
|
|
791
|
+
/** Per-session projection baselines (follow snapshot + control frames; W2). */
|
|
792
|
+
projections = /* @__PURE__ */ new Map();
|
|
793
|
+
/** Workspace registry cache from workspace/follow (W2; authority = host). */
|
|
794
|
+
workspaces = [];
|
|
795
|
+
/** Waiters resolved by the first workspace/follow baseline (one-shot). */
|
|
796
|
+
workspaceBaselineWaiters = /* @__PURE__ */ new Set();
|
|
797
|
+
/** Subscribes parked while the carrier is CONNECTING (flushed on open). */
|
|
798
|
+
pendingSubscribes = /* @__PURE__ */ new Set();
|
|
453
799
|
reconnectTimer;
|
|
454
|
-
disposed = false;
|
|
455
800
|
/** Wave 4.2 — consecutive failed generations; drives the backoff ladder. */
|
|
456
801
|
reconnectAttempt = 0;
|
|
457
|
-
/** Reap grace (see DshConnectionOptions.answerReapGraceMs). */
|
|
458
|
-
answerReapGraceMs;
|
|
459
|
-
answerReapTimer;
|
|
460
|
-
/** Stale-generation retry deadline (see DshConnectionOptions.staleGenerationRetryMs). */
|
|
461
|
-
staleGenerationRetryMs;
|
|
462
|
-
constructor(opts = {}) {
|
|
463
|
-
this.baseUrl = opts.baseUrl ?? dshBaseUrl();
|
|
464
|
-
this.fetchFn = opts.fetchFn ?? fetch;
|
|
465
|
-
this.wsFactory = opts.wsFactory ?? ((url, headers) => new WebSocket(url, headers === void 0 ? void 0 : { headers }));
|
|
466
|
-
this.auth = opts.auth ?? new DshAuth({
|
|
467
|
-
baseUrl: this.baseUrl,
|
|
468
|
-
log: opts.log
|
|
469
|
-
});
|
|
470
|
-
this.rpcIdFactory = opts.rpcIdFactory ?? (() => crypto.randomUUID());
|
|
471
|
-
this.backoff = opts.backoffScheduleMs ?? [
|
|
472
|
-
1e3,
|
|
473
|
-
2e3,
|
|
474
|
-
5e3,
|
|
475
|
-
1e4,
|
|
476
|
-
3e4
|
|
477
|
-
];
|
|
478
|
-
this.answerReapGraceMs = opts.answerReapGraceMs ?? 3e3;
|
|
479
|
-
this.staleGenerationRetryMs = opts.staleGenerationRetryMs ?? 5e3;
|
|
480
|
-
this.log = opts.log ?? (() => {});
|
|
481
|
-
}
|
|
482
802
|
/**
|
|
483
803
|
* Open the stream generation (idempotent): ONE cookie-authenticated
|
|
484
804
|
* remote.mux socket (0.1.2 — events.mux/events.host are gone), with every
|
|
@@ -486,22 +806,22 @@ var DshConnection = class {
|
|
|
486
806
|
* the $events forwarded-event stream (approvals/questions).
|
|
487
807
|
*/
|
|
488
808
|
ensureDownlinks() {
|
|
489
|
-
if (this.disposed || this.wsMux) return;
|
|
809
|
+
if (this.conn.disposed || this.wsMux) return;
|
|
490
810
|
++this.generation;
|
|
491
|
-
this.log(`dsh-connection: opening remote.mux generation ${this.generation}`);
|
|
811
|
+
this.conn.log(`dsh-connection: opening remote.mux generation ${this.generation}`);
|
|
492
812
|
this.wsMux = this.openDownlink((socket) => this.onMuxOpen(socket));
|
|
493
|
-
this.seedRunning();
|
|
813
|
+
this.conn.sessions.seedRunning();
|
|
494
814
|
}
|
|
495
815
|
openDownlink(attach) {
|
|
496
|
-
const url = this.baseUrl.replace(/^http/, "ws") + DSH_REMOTE_MUX_WS_PATH;
|
|
497
|
-
const headers = this.auth.cookieHeader();
|
|
498
|
-
const socket = this.wsFactory(url, headers === void 0 ? void 0 : { cookie: headers });
|
|
499
|
-
this.auth.ensureCookie().catch(() => null);
|
|
816
|
+
const url = this.conn.baseUrl.replace(/^http/, "ws") + DSH_REMOTE_MUX_WS_PATH;
|
|
817
|
+
const headers = this.conn.auth.cookieHeader();
|
|
818
|
+
const socket = this.conn.wsFactory(url, headers === void 0 ? void 0 : { cookie: headers });
|
|
819
|
+
this.conn.auth.ensureCookie().catch(() => null);
|
|
500
820
|
socket.addEventListener("message", (e) => {
|
|
501
821
|
try {
|
|
502
822
|
this.onStreamFrame(JSON.parse(String(e.data)));
|
|
503
823
|
} catch (err) {
|
|
504
|
-
this.log("dsh-connection: dropping malformed frame", err);
|
|
824
|
+
this.conn.log("dsh-connection: dropping malformed frame", err);
|
|
505
825
|
}
|
|
506
826
|
});
|
|
507
827
|
socket.addEventListener("open", () => {
|
|
@@ -516,7 +836,7 @@ var DshConnection = class {
|
|
|
516
836
|
onMuxOpen(socket) {
|
|
517
837
|
this.streamSeq += 1;
|
|
518
838
|
this.pendingSubscribes.clear();
|
|
519
|
-
for (const sessionId of this.
|
|
839
|
+
for (const sessionId of this.conn.sessions.bufferKeys()) this.openFollowWithProbe(socket, sessionId);
|
|
520
840
|
this.openEventStream(socket);
|
|
521
841
|
this.openStateStream(socket, `workspaces-${String(this.streamSeq)}`, "workspace/follow");
|
|
522
842
|
this.openStateStream(socket, `control-${String(this.streamSeq)}`, "session/control");
|
|
@@ -527,7 +847,7 @@ var DshConnection = class {
|
|
|
527
847
|
}
|
|
528
848
|
openFollowStream(socket, sessionId) {
|
|
529
849
|
this.sendStreamOpen(socket, followStreamId(sessionId, this.streamSeq), "session/follow", { args: { request: {
|
|
530
|
-
address: this.addressFor(sessionId),
|
|
850
|
+
address: this.conn.relay.addressFor(sessionId),
|
|
531
851
|
maxMessages: 200,
|
|
532
852
|
assistantStream: true
|
|
533
853
|
} } });
|
|
@@ -547,20 +867,20 @@ var DshConnection = class {
|
|
|
547
867
|
}
|
|
548
868
|
/** Either socket dying rebuilds the whole generation (guide Part 1). */
|
|
549
869
|
onDownlinkClosed() {
|
|
550
|
-
if (this.disposed) return;
|
|
870
|
+
if (this.conn.disposed) return;
|
|
551
871
|
if (!this.wsMux) return;
|
|
552
|
-
this.log("dsh-connection: remote.mux died — rebuilding generation");
|
|
553
|
-
this.fireCatalogInvalidated("all");
|
|
872
|
+
this.conn.log("dsh-connection: remote.mux died — rebuilding generation");
|
|
873
|
+
this.conn.relay.fireCatalogInvalidated("all");
|
|
554
874
|
this.teardownSockets();
|
|
555
875
|
this.scheduleReconnect();
|
|
556
876
|
}
|
|
557
877
|
scheduleReconnect() {
|
|
558
|
-
if (this.disposed) return;
|
|
559
|
-
const rung = Math.min(this.reconnectAttempt, this.backoff.length - 1);
|
|
560
|
-
const delay = this.backoff[rung];
|
|
878
|
+
if (this.conn.disposed) return;
|
|
879
|
+
const rung = Math.min(this.reconnectAttempt, this.conn.backoff.length - 1);
|
|
880
|
+
const delay = this.conn.backoff[rung];
|
|
561
881
|
if (this.reconnectTimer !== void 0) clearTimeout(this.reconnectTimer);
|
|
562
882
|
this.reconnectTimer = setTimeout(() => {
|
|
563
|
-
if (this.disposed) return;
|
|
883
|
+
if (this.conn.disposed) return;
|
|
564
884
|
this.reconnectAttempt += 1;
|
|
565
885
|
this.ensureDownlinks();
|
|
566
886
|
}, delay);
|
|
@@ -598,7 +918,7 @@ var DshConnection = class {
|
|
|
598
918
|
else if (frame.streamId.startsWith("control-")) this.onControlItem(value);
|
|
599
919
|
return;
|
|
600
920
|
}
|
|
601
|
-
if (frame.type === "error") this.log(`dsh-connection: stream ${frame.streamId} failed`, frame.error?.code, frame.error?.message);
|
|
921
|
+
if (frame.type === "error") this.conn.log(`dsh-connection: stream ${frame.streamId} failed`, frame.error?.code, frame.error?.message);
|
|
602
922
|
this.streamSockets.delete(frame.streamId);
|
|
603
923
|
}
|
|
604
924
|
/** session/follow item: snapshot seeds the baseline; events append; the
|
|
@@ -610,30 +930,30 @@ var DshConnection = class {
|
|
|
610
930
|
if (sessionId === null) return;
|
|
611
931
|
const snapshot = value;
|
|
612
932
|
const events = expandRecords(snapshot.records ?? []);
|
|
613
|
-
for (const event of events) this.appendEvent(sessionId, event);
|
|
614
|
-
this.seedSnapshotCursor(sessionId, snapshot.cursor);
|
|
933
|
+
for (const event of events) this.conn.sessions.appendEvent(sessionId, event);
|
|
934
|
+
this.conn.sessions.seedSnapshotCursor(sessionId, snapshot.cursor);
|
|
615
935
|
if (snapshot.projections !== null && typeof snapshot.projections === "object") this.projections.set(sessionId, snapshot.projections);
|
|
616
|
-
this.seedLiveTailFromBaseline(sessionId, snapshot.assistantStream);
|
|
936
|
+
this.conn.sessions.seedLiveTailFromBaseline(sessionId, snapshot.assistantStream);
|
|
617
937
|
return;
|
|
618
938
|
}
|
|
619
939
|
if (value.type === "assistant-stream") {
|
|
620
940
|
const sessionId = this.sessionIdOfStream(streamId);
|
|
621
941
|
const frame = value.frame;
|
|
622
|
-
if (sessionId !== null && frame !== void 0) this.onAssistantStreamFrame(sessionId, frame);
|
|
942
|
+
if (sessionId !== null && frame !== void 0) this.conn.sessions.onAssistantStreamFrame(sessionId, frame);
|
|
623
943
|
return;
|
|
624
944
|
}
|
|
625
945
|
if (value.type === "event") {
|
|
626
946
|
const sessionId = this.sessionIdOfStream(streamId);
|
|
627
947
|
const entry = value;
|
|
628
|
-
if (sessionId !== null && entry.event !== void 0) for (const event of expandEntry(entry.event)) this.appendEvent(sessionId, event);
|
|
948
|
+
if (sessionId !== null && entry.event !== void 0) for (const event of expandEntry(entry.event)) this.conn.sessions.appendEvent(sessionId, event);
|
|
629
949
|
}
|
|
630
950
|
}
|
|
631
951
|
/** $events item: forwarded Cordis event frames (waterfall = answerable). */
|
|
632
952
|
onEventItem(value) {
|
|
633
953
|
if (value.type === "ready") {
|
|
634
954
|
if (typeof value.clientId === "string") this.eventClientId = value.clientId;
|
|
635
|
-
this.log("dsh-connection: $events stream ready");
|
|
636
|
-
this.armAnswerReap();
|
|
955
|
+
this.conn.log("dsh-connection: $events stream ready");
|
|
956
|
+
this.conn.relay.armAnswerReap();
|
|
637
957
|
return;
|
|
638
958
|
}
|
|
639
959
|
if (value.type === "waterfall") {
|
|
@@ -643,7 +963,7 @@ var DshConnection = class {
|
|
|
643
963
|
const request = value.request !== null && typeof value.request === "object" ? value.request : {};
|
|
644
964
|
if (eventId === void 0) return;
|
|
645
965
|
if (event === "approval/request") {
|
|
646
|
-
this.registerPending({
|
|
966
|
+
this.conn.relay.registerPending({
|
|
647
967
|
type: "approval/requested",
|
|
648
968
|
sessionId: sessionId ?? "",
|
|
649
969
|
...request
|
|
@@ -651,43 +971,43 @@ var DshConnection = class {
|
|
|
651
971
|
return;
|
|
652
972
|
}
|
|
653
973
|
if (event === "user-questions/request") {
|
|
654
|
-
this.registerPending({
|
|
974
|
+
this.conn.relay.registerPending({
|
|
655
975
|
type: "question/requested",
|
|
656
976
|
sessionId: sessionId ?? "",
|
|
657
977
|
...request
|
|
658
978
|
}, eventId);
|
|
659
979
|
return;
|
|
660
980
|
}
|
|
661
|
-
this.log("dsh-connection: forwarded waterfall ignored", event);
|
|
981
|
+
this.conn.log("dsh-connection: forwarded waterfall ignored", event);
|
|
662
982
|
return;
|
|
663
983
|
}
|
|
664
984
|
if (value.type === "cancel") {
|
|
665
985
|
const eventId = typeof value.eventId === "string" ? value.eventId : void 0;
|
|
666
|
-
if (eventId !== void 0) this.withdrawPending(eventId);
|
|
667
|
-
this.log("dsh-connection: forwarded waterfall cancelled");
|
|
986
|
+
if (eventId !== void 0) this.conn.relay.withdrawPending(eventId);
|
|
987
|
+
this.conn.log("dsh-connection: forwarded waterfall cancelled");
|
|
668
988
|
return;
|
|
669
989
|
}
|
|
670
990
|
if (value.type === "settle") {
|
|
671
991
|
const eventId = typeof value.eventId === "string" ? value.eventId : void 0;
|
|
672
992
|
const outcome = typeof value.outcome === "string" ? value.outcome : "unknown";
|
|
673
|
-
if (eventId !== void 0) this.withdrawPending(eventId, outcome);
|
|
993
|
+
if (eventId !== void 0) this.conn.relay.withdrawPending(eventId, outcome);
|
|
674
994
|
return;
|
|
675
995
|
}
|
|
676
996
|
if (value.type === "emit") {
|
|
677
997
|
if (value.event === "api-session/status" && Array.isArray(value.args)) {
|
|
678
998
|
const [sessionId, running] = value.args;
|
|
679
|
-
if (typeof sessionId === "string") this.
|
|
999
|
+
if (typeof sessionId === "string") this.conn.sessions.setRunning(sessionId, running === true);
|
|
680
1000
|
return;
|
|
681
1001
|
}
|
|
682
1002
|
if (value.event === "commands/change") {
|
|
683
|
-
this.fireCatalogInvalidated("all");
|
|
1003
|
+
this.conn.relay.fireCatalogInvalidated("all");
|
|
684
1004
|
return;
|
|
685
1005
|
}
|
|
686
1006
|
if (value.event === "agent-preset/selected" && typeof value.agentId === "string") {
|
|
687
|
-
this.fireCatalogInvalidated(value.agentId);
|
|
1007
|
+
this.conn.relay.fireCatalogInvalidated(value.agentId);
|
|
688
1008
|
return;
|
|
689
1009
|
}
|
|
690
|
-
this.log("dsh-connection: forwarded emit ignored", value.event);
|
|
1010
|
+
this.conn.log("dsh-connection: forwarded emit ignored", value.event);
|
|
691
1011
|
return;
|
|
692
1012
|
}
|
|
693
1013
|
}
|
|
@@ -712,7 +1032,7 @@ var DshConnection = class {
|
|
|
712
1032
|
});
|
|
713
1033
|
for (const waiter of this.workspaceBaselineWaiters) waiter.resolve();
|
|
714
1034
|
this.workspaceBaselineWaiters.clear();
|
|
715
|
-
this.log(`dsh-connection: workspace baseline (${this.workspaces.length} rows)`);
|
|
1035
|
+
this.conn.log(`dsh-connection: workspace baseline (${this.workspaces.length} rows)`);
|
|
716
1036
|
return;
|
|
717
1037
|
}
|
|
718
1038
|
if (value.type === "upsert") {
|
|
@@ -743,7 +1063,7 @@ var DshConnection = class {
|
|
|
743
1063
|
if (value.type === "baseline") {
|
|
744
1064
|
const baseline = value.value ?? {};
|
|
745
1065
|
for (const [sessionId, block] of Object.entries(baseline.projections ?? {})) if (block !== null && typeof block === "object") this.projections.set(sessionId, block);
|
|
746
|
-
this.log(`dsh-connection: control baseline (${this.projections.size} sessions)`);
|
|
1066
|
+
this.conn.log(`dsh-connection: control baseline (${this.projections.size} sessions)`);
|
|
747
1067
|
return;
|
|
748
1068
|
}
|
|
749
1069
|
if (value.type === "projection" && typeof value.sessionId === "string" && typeof value.key === "string") {
|
|
@@ -766,264 +1086,26 @@ var DshConnection = class {
|
|
|
766
1086
|
const cut = rest.lastIndexOf("-");
|
|
767
1087
|
return cut > 0 ? rest.slice(0, cut) : null;
|
|
768
1088
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
this.
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
case "session/event":
|
|
790
|
-
if (frame.sessionId && frame.event) this.appendEvent(frame.sessionId, frame.event);
|
|
791
|
-
return;
|
|
792
|
-
case "approval/requested":
|
|
793
|
-
case "question/requested":
|
|
794
|
-
this.registerPending(frame, rpcId);
|
|
795
|
-
return;
|
|
796
|
-
case "approval/resolved":
|
|
797
|
-
case "question/resolved":
|
|
798
|
-
this.registerSettlement(frame);
|
|
799
|
-
return;
|
|
800
|
-
default: this.log("dsh-connection: mux frame ignored", frame?.type);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
onHostFrame(payload) {
|
|
804
|
-
const frame = payload;
|
|
805
|
-
if (frame?.type === "host/session-status" && typeof frame.sessionId === "string") {
|
|
806
|
-
this.runningSeedRaces.add(frame.sessionId);
|
|
807
|
-
this.running.set(frame.sessionId, Boolean(frame.running));
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* 2026-08-26 running re-baseline: DSH pushes host/session-status only on
|
|
812
|
-
* transitions and the host stream sends no initial snapshot on (re)open —
|
|
813
|
-
* a downlink rebuilt mid-turn never sees the running:true flip that
|
|
814
|
-
* happened before it existed (the live bug: events kept flowing on the
|
|
815
|
-
* mux while the streaming indicator said idle). session.list is the host's
|
|
816
|
-
* documented reconnect baseline, so every generation opens with ONE list
|
|
817
|
-
* RPC seeding the whole map — which also prunes entries for sessions that
|
|
818
|
-
* died while disconnected. Pushes that land while the RPC is in flight win
|
|
819
|
-
* over the snapshot (they are strictly newer transitions); a seed from a
|
|
820
|
-
* superseded generation is dropped entirely.
|
|
821
|
-
*/
|
|
822
|
-
async seedRunning() {
|
|
823
|
-
const generation = this.generation;
|
|
824
|
-
this.runningSeedRaces = /* @__PURE__ */ new Set();
|
|
825
|
-
try {
|
|
826
|
-
const { items } = await this.listSessions();
|
|
827
|
-
if (this.disposed || generation !== this.generation) return;
|
|
828
|
-
const races = this.runningSeedRaces;
|
|
829
|
-
const next = /* @__PURE__ */ new Map();
|
|
830
|
-
for (const [id, running] of this.running) if (races.has(id)) next.set(id, running);
|
|
831
|
-
for (const row of items) if (!races.has(row.sessionId)) next.set(row.sessionId, row.running);
|
|
832
|
-
this.running = next;
|
|
833
|
-
this.log(`dsh-connection: running map re-baselined (${items.length} sessions)`);
|
|
834
|
-
} catch (err) {
|
|
835
|
-
this.log("dsh-connection: running re-baseline failed — pushes stay authoritative", err);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
bufferFor(sessionId) {
|
|
839
|
-
let buf = this.buffers.get(sessionId);
|
|
840
|
-
if (!buf) {
|
|
841
|
-
buf = {
|
|
842
|
-
entries: [],
|
|
843
|
-
lastSeq: -1,
|
|
844
|
-
coverageFloor: -1
|
|
845
|
-
};
|
|
846
|
-
this.buffers.set(sessionId, buf);
|
|
847
|
-
}
|
|
848
|
-
return buf;
|
|
849
|
-
}
|
|
850
|
-
seedBaseline(sessionId, lastSeq) {
|
|
851
|
-
const buf = this.bufferFor(sessionId);
|
|
852
|
-
if (lastSeq > buf.lastSeq && buf.entries.length === 0) {
|
|
853
|
-
buf.lastSeq = lastSeq;
|
|
854
|
-
buf.coverageFloor = lastSeq;
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
if (buf.lastSeq >= 0 && lastSeq < buf.lastSeq) {
|
|
858
|
-
buf.entries.length = 0;
|
|
859
|
-
buf.lastSeq = lastSeq;
|
|
860
|
-
buf.coverageFloor = lastSeq;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
/**
|
|
864
|
-
* Snapshot cursor seed (0.1.2). A snapshot BELOW the buffered high-water
|
|
865
|
-
* mark means the upstream was rebuilt (stream restart): stale entries
|
|
866
|
-
* belong to the dead instance — reset the buffer to the snapshot truth
|
|
867
|
-
* (Wave 2 parity with the 0.1.1 rewound-baseline semantics).
|
|
868
|
-
*/
|
|
869
|
-
seedSnapshotCursor(sessionId, cursor) {
|
|
870
|
-
const buf = this.bufferFor(sessionId);
|
|
871
|
-
if (cursor > buf.lastSeq) {
|
|
872
|
-
buf.lastSeq = cursor;
|
|
873
|
-
buf.coverageFloor = cursor;
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
if (buf.lastSeq >= 0 && cursor < buf.lastSeq) {
|
|
877
|
-
buf.entries.length = 0;
|
|
878
|
-
buf.lastSeq = cursor;
|
|
879
|
-
buf.coverageFloor = cursor;
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
appendEvent(sessionId, event) {
|
|
883
|
-
const buf = this.bufferFor(sessionId);
|
|
884
|
-
if (event.seq > buf.lastSeq) {
|
|
885
|
-
if (buf.lastSeq >= 0 && event.seq > buf.lastSeq + 1) {
|
|
886
|
-
buf.coverageFloor = Math.max(buf.coverageFloor, buf.lastSeq);
|
|
887
|
-
this.log(`dsh-connection: seq gap for ${sessionId} (${buf.lastSeq} → ${event.seq}) — resync required below ${event.seq}`);
|
|
888
|
-
}
|
|
889
|
-
buf.lastSeq = event.seq;
|
|
890
|
-
buf.entries.push(event);
|
|
891
|
-
const cap = ringCapacity();
|
|
892
|
-
if (buf.entries.length > cap) buf.entries.splice(0, buf.entries.length - cap);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* Fold one assistant-stream frame into the session's live tail. Seq-less
|
|
897
|
-
* presentation state: never touches lastSeq/coverage — the durable
|
|
898
|
-
* cursor algebra stays event-entry-only. The host keeps ONE active
|
|
899
|
-
* attempt per session and drops its own accumulator on any continuity
|
|
900
|
-
* break (foreign attemptId, dense-index gap), so mirrors do the same.
|
|
901
|
-
*/
|
|
902
|
-
onAssistantStreamFrame(sessionId, frame) {
|
|
903
|
-
if (frame.type === "start") {
|
|
904
|
-
this.liveTails.set(sessionId, {
|
|
905
|
-
attemptId: frame.attemptId,
|
|
906
|
-
turn: frame.turn,
|
|
907
|
-
step: frame.step,
|
|
908
|
-
startedAfterSeq: frame.startedAfterSeq,
|
|
909
|
-
startedAt: Date.now(),
|
|
910
|
-
lastTime: Date.now(),
|
|
911
|
-
text: "",
|
|
912
|
-
reasoning: "",
|
|
913
|
-
nextIndex: 0
|
|
914
|
-
});
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
const tail = this.liveTails.get(sessionId);
|
|
918
|
-
if (tail === void 0) return;
|
|
919
|
-
if (frame.type === "chunk") {
|
|
920
|
-
if (frame.attemptId !== tail.attemptId || frame.index !== tail.nextIndex) {
|
|
921
|
-
this.liveTails.delete(sessionId);
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
const kind = frame.chunk?.type;
|
|
925
|
-
if (kind === "text-delta" && typeof frame.chunk?.text === "string") tail.text += frame.chunk.text;
|
|
926
|
-
else if (kind === "reasoning-delta" && typeof frame.chunk?.text === "string") tail.reasoning += frame.chunk.text;
|
|
927
|
-
tail.nextIndex += 1;
|
|
928
|
-
if (typeof frame.time === "number") tail.lastTime = frame.time;
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
this.liveTails.delete(sessionId);
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Seed the live tail from a follow-opening snapshot's assistantStream
|
|
935
|
-
* baseline (mux-drop reopen mid-attempt): expand the compact stream
|
|
936
|
-
* records' text/reasoning runs into the tail. No activeAttempt → clear
|
|
937
|
-
* any remembered tail — the fresh opening is authoritative for what is
|
|
938
|
-
* still in flight.
|
|
939
|
-
*/
|
|
940
|
-
seedLiveTailFromBaseline(sessionId, baseline) {
|
|
941
|
-
const active = baseline?.activeAttempt;
|
|
942
|
-
if (active === void 0) {
|
|
943
|
-
this.liveTails.delete(sessionId);
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
const tail = {
|
|
947
|
-
attemptId: active.attemptId,
|
|
948
|
-
turn: active.turn,
|
|
949
|
-
step: active.step,
|
|
950
|
-
startedAfterSeq: active.startedAfterSeq,
|
|
951
|
-
startedAt: Date.now(),
|
|
952
|
-
lastTime: Date.now(),
|
|
953
|
-
text: "",
|
|
954
|
-
reasoning: "",
|
|
955
|
-
nextIndex: active.nextIndex
|
|
956
|
-
};
|
|
957
|
-
for (const record of active.stream ?? []) {
|
|
958
|
-
if (record.type === "text-chunks" || record.type === "reasoning-chunks") {
|
|
959
|
-
const texts = record.texts;
|
|
960
|
-
const joined = Array.isArray(texts) ? texts.filter((t) => typeof t === "string").join("") : "";
|
|
961
|
-
if (record.type === "text-chunks") tail.text += joined;
|
|
962
|
-
else tail.reasoning += joined;
|
|
963
|
-
}
|
|
964
|
-
const times = compactRecordTailTime(record);
|
|
965
|
-
if (times !== void 0) tail.lastTime = times;
|
|
966
|
-
}
|
|
967
|
-
this.liveTails.set(sessionId, tail);
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* The session's live tail for the events poll, or null when no attempt
|
|
971
|
-
* is in flight. Served with the FULL accumulated text each poll — the
|
|
972
|
-
* client replaces its streaming bubble wholesale (no cross-poll fragment
|
|
973
|
-
* bookkeeping, so reconnects/retries converge from the served truth).
|
|
974
|
-
*/
|
|
975
|
-
liveAssistantStream(sessionId) {
|
|
976
|
-
const tail = this.liveTails.get(sessionId);
|
|
977
|
-
if (tail === void 0) return null;
|
|
978
|
-
return {
|
|
979
|
-
id: `a:${String(tail.turn)}:${String(tail.step)}`,
|
|
980
|
-
turn: tail.turn,
|
|
981
|
-
step: tail.step,
|
|
982
|
-
seq: tail.startedAfterSeq,
|
|
983
|
-
time: tail.lastTime,
|
|
984
|
-
text: tail.text,
|
|
985
|
-
reasoning: tail.reasoning
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
/**
|
|
989
|
-
* Delta slice: events with seq > n, plus current lastSeq/running.
|
|
990
|
-
* Wave 4.2: `gap` is true when the caller's `since` mark fell into a hole
|
|
991
|
-
* the buffer no longer covers (mux drop). The response then carries the
|
|
992
|
-
* resync flag so the client re-reads from the ledger instead of trusting
|
|
993
|
-
* a partial delta (BC-4).
|
|
994
|
-
*/
|
|
995
|
-
eventsSince(sessionId, n) {
|
|
996
|
-
const buf = this.bufferFor(sessionId);
|
|
997
|
-
const events = buf.entries.filter((e) => e.seq > n);
|
|
998
|
-
const firstDelta = events[0]?.seq;
|
|
999
|
-
const gap = firstDelta !== void 0 && firstDelta > n + 1;
|
|
1000
|
-
return {
|
|
1001
|
-
events,
|
|
1002
|
-
lastSeq: buf.lastSeq,
|
|
1003
|
-
running: this.running.get(sessionId) ?? false,
|
|
1004
|
-
gap
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
/**
|
|
1008
|
-
* Subscribe one session (0.1.2): create its buffer and open a
|
|
1009
|
-
* session/follow logical stream on the live carrier (or mark it for the
|
|
1010
|
-
* next generation). Idempotent; the snapshot frame seeds the baseline.
|
|
1011
|
-
* A repeat call while THIS generation already holds the session's follow
|
|
1012
|
-
* stream is a no-op — the api-dsh events route calls subscribe on every
|
|
1013
|
-
* poll, and reopening would replace the stream (same streamId → fresh
|
|
1014
|
-
* snapshot) on every tick. A stream 'end'/'error' frame drops the
|
|
1015
|
-
* tracking entry so the next poll reopens and self-heals.
|
|
1016
|
-
*/
|
|
1017
|
-
subscribe(sessionId) {
|
|
1018
|
-
this.bufferFor(sessionId);
|
|
1019
|
-
const socket = this.wsMux;
|
|
1020
|
-
if (socket === void 0) return;
|
|
1021
|
-
if (this.opened === false) {
|
|
1022
|
-
this.pendingSubscribes.add(sessionId);
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
if (this.streamSockets.has(followStreamId(sessionId, this.streamSeq))) return;
|
|
1026
|
-
this.openFollowWithProbe(socket, sessionId);
|
|
1089
|
+
/**
|
|
1090
|
+
* Subscribe one session (0.1.2): create its buffer and open a
|
|
1091
|
+
* session/follow logical stream on the live carrier (or mark it for the
|
|
1092
|
+
* next generation). Idempotent; the snapshot frame seeds the baseline.
|
|
1093
|
+
* A repeat call while THIS generation already holds the session's follow
|
|
1094
|
+
* stream is a no-op — the api-dsh events route calls subscribe on every
|
|
1095
|
+
* poll, and reopening would replace the stream (same streamId → fresh
|
|
1096
|
+
* snapshot) on every tick. A stream 'end'/'error' frame drops the
|
|
1097
|
+
* tracking entry so the next poll reopens and self-heals.
|
|
1098
|
+
*/
|
|
1099
|
+
subscribe(sessionId) {
|
|
1100
|
+
this.conn.sessions.bufferFor(sessionId);
|
|
1101
|
+
const socket = this.wsMux;
|
|
1102
|
+
if (socket === void 0) return;
|
|
1103
|
+
if (this.opened === false) {
|
|
1104
|
+
this.pendingSubscribes.add(sessionId);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (this.streamSockets.has(followStreamId(sessionId, this.streamSeq))) return;
|
|
1108
|
+
this.openFollowWithProbe(socket, sessionId);
|
|
1027
1109
|
}
|
|
1028
1110
|
/**
|
|
1029
1111
|
* Open the follow stream with the best-known address NOW (sync, the
|
|
@@ -1033,8 +1115,8 @@ var DshConnection = class {
|
|
|
1033
1115
|
*/
|
|
1034
1116
|
openFollowWithProbe(socket, sessionId) {
|
|
1035
1117
|
this.openFollowStream(socket, sessionId);
|
|
1036
|
-
if (!this.subagentHints.has(sessionId) && !this.hintProbed.has(sessionId)) {
|
|
1037
|
-
this.hintProbed.add(sessionId);
|
|
1118
|
+
if (!this.conn.relay.subagentHints.has(sessionId) && !this.conn.relay.hintProbed.has(sessionId)) {
|
|
1119
|
+
this.conn.relay.hintProbed.add(sessionId);
|
|
1038
1120
|
this.probeHintThenSubscribe(sessionId, socket);
|
|
1039
1121
|
}
|
|
1040
1122
|
}
|
|
@@ -1046,10 +1128,10 @@ var DshConnection = class {
|
|
|
1046
1128
|
* reopens every buffer with the then-current addresses. */
|
|
1047
1129
|
async probeHintThenSubscribe(sessionId, socket) {
|
|
1048
1130
|
try {
|
|
1049
|
-
if (!this.subagentHints.has(sessionId)) await this.rawListSessions();
|
|
1131
|
+
if (!this.conn.relay.subagentHints.has(sessionId)) await this.conn.relay.rawListSessions();
|
|
1050
1132
|
} catch {}
|
|
1051
|
-
if (this.disposed || this.wsMux !== socket || this.opened === false) return;
|
|
1052
|
-
if (this.subagentHints.has(sessionId)) this.openFollowStream(socket, sessionId);
|
|
1133
|
+
if (this.conn.disposed || this.wsMux !== socket || this.opened === false) return;
|
|
1134
|
+
if (this.conn.relay.subagentHints.has(sessionId)) this.openFollowStream(socket, sessionId);
|
|
1053
1135
|
}
|
|
1054
1136
|
/**
|
|
1055
1137
|
* Per-session projection value (W2): follow-snapshot + control-baseline
|
|
@@ -1096,55 +1178,131 @@ var DshConnection = class {
|
|
|
1096
1178
|
this.workspaceBaselineWaiters.delete(waiter);
|
|
1097
1179
|
return this.workspaces.length > 0;
|
|
1098
1180
|
}
|
|
1099
|
-
/**
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1181
|
+
/** The workspace registry (2026-08-23 registry parity) — the AUTHORITY
|
|
1182
|
+
* for DSI's workspace pills/chips: a deleted workspace disappears, and
|
|
1183
|
+
* sessions outside every registry entry render cue-less (DSH's
|
|
1184
|
+
* Ungrouped). Membership is sessionIds, not cwd matching. */
|
|
1185
|
+
async listWorkspaces() {
|
|
1186
|
+
if (!await this.awaitWorkspaceBaseline()) this.conn.log("dsh-connection: workspace baseline did not land in time — honest empty");
|
|
1187
|
+
return { items: [...this.workspaces] };
|
|
1105
1188
|
}
|
|
1106
|
-
/**
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
this.
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
if (typeof rpcId !== "string" || rpcId.length === 0) {
|
|
1114
|
-
this.log("dsh-connection: answerable frame without rpcId — ignored", frame?.type);
|
|
1115
|
-
return;
|
|
1189
|
+
/** Cancel a scheduled reconnect (stale-generation answer retry path, W4). */
|
|
1190
|
+
cancelReconnect() {
|
|
1191
|
+
if (this.reconnectTimer !== void 0) {
|
|
1192
|
+
clearTimeout(this.reconnectTimer);
|
|
1193
|
+
this.reconnectTimer = void 0;
|
|
1116
1194
|
}
|
|
1117
|
-
const kind = frame.type === "approval/requested" ? "approval" : "question";
|
|
1118
|
-
this.pendingAnswers.set(rpcId, {
|
|
1119
|
-
rpcId,
|
|
1120
|
-
sessionId,
|
|
1121
|
-
kind,
|
|
1122
|
-
body: frame,
|
|
1123
|
-
receivedAt: Date.now(),
|
|
1124
|
-
deliveredGen: this.generation
|
|
1125
|
-
});
|
|
1126
|
-
this.log(`dsh-connection: pending ${kind} registered (${rpcId}) for ${sessionId}`);
|
|
1127
1195
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
this.
|
|
1196
|
+
};
|
|
1197
|
+
//#endregion
|
|
1198
|
+
//#region src/lib/server/connection/rpc-relay.ts
|
|
1199
|
+
/**
|
|
1200
|
+
* connection/rpc-relay — the RPC relay + answer registry (ADR-0022 W4,
|
|
1201
|
+
* ADR-0019 move-whole): the POC-3 pending-answer/settlement registries and
|
|
1202
|
+
* their replay reap, the catalog-invalidation listener registry, the
|
|
1203
|
+
* subagent parent-address hints, and every unary RPC forward (all bytes via
|
|
1204
|
+
* dsh-rpc helpers; the transport POST itself stays in the shell's rpc()).
|
|
1205
|
+
*/
|
|
1206
|
+
var RpcRelay = class {
|
|
1207
|
+
conn;
|
|
1208
|
+
ringCapacity;
|
|
1209
|
+
answerReapTimer;
|
|
1210
|
+
answerReapGraceMs;
|
|
1211
|
+
staleGenerationRetryMs;
|
|
1212
|
+
constructor(conn, ringCapacity, opts) {
|
|
1213
|
+
this.conn = conn;
|
|
1214
|
+
this.ringCapacity = ringCapacity;
|
|
1215
|
+
this.answerReapGraceMs = opts.answerReapGraceMs;
|
|
1216
|
+
this.staleGenerationRetryMs = opts.staleGenerationRetryMs;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* POC-3 W1 — answerable frames arrive as ServerRequest pushes with a
|
|
1220
|
+
* stable rpcId. Mux-open REPLAYS still-pending requested frames with the
|
|
1221
|
+
* same rpcId (api-proxy.ts replay loop), so the registry must be
|
|
1222
|
+
* idempotent by rpcId (BC-C): a replay overwrites, never duplicates.
|
|
1223
|
+
*/
|
|
1224
|
+
pendingAnswers = /* @__PURE__ */ new Map();
|
|
1225
|
+
/** Settlement ring: last outcome per rpcId, delivered-then-pruned. */
|
|
1226
|
+
settledAnswers = /* @__PURE__ */ new Map();
|
|
1227
|
+
/** Catalog-invalidation registry (Slash Menu W1) — listeners see every arm. */
|
|
1228
|
+
catalogInvalidated = /* @__PURE__ */ new Set();
|
|
1229
|
+
/**
|
|
1230
|
+
* Parent-address hints for subagent-origin sessions (SubagentAddressHint),
|
|
1231
|
+
* folded from session.list rows by rawListSessions.
|
|
1232
|
+
*/
|
|
1233
|
+
subagentHints = /* @__PURE__ */ new Map();
|
|
1234
|
+
/** Session-ids whose hint absence was already probed with a list pass
|
|
1235
|
+
* (one probe per session lifetime — ordinary sessions never re-probe). */
|
|
1236
|
+
hintProbed = /* @__PURE__ */ new Set();
|
|
1237
|
+
/**
|
|
1238
|
+
* Fork-child display titles healed off the wire (2026-09-14, host gap
|
|
1239
|
+
* DSI-side): the host's session.list serves NO projections column for a
|
|
1240
|
+
* cold seeded (fork child) row — session-controller list.ts
|
|
1241
|
+
* projectionsFor short-circuits isSeeded headers because the projection
|
|
1242
|
+
* cache is keyed by the inherited cut a listed header does not carry.
|
|
1243
|
+
* The real title (what the panel shows once the session attaches) lives
|
|
1244
|
+
* in the child's ledger: a child that ran its own turns carries a NEW
|
|
1245
|
+
* session/title event near the tail, so ONE bounded tail-page read per
|
|
1246
|
+
* untitled fork row recovers it. The result caches for the process
|
|
1247
|
+
* lifetime (a retitle lands through the normal rename/live paths, which
|
|
1248
|
+
* serve the title directly). The parent row's title is the FALLBACK for
|
|
1249
|
+
* children the tail page shows never retitled — the inherited title IS
|
|
1250
|
+
* the parent's at fork time; stale only when the parent was renamed
|
|
1251
|
+
* after the fork. Absent hints and absent parents leave the row
|
|
1252
|
+
* honestly untitled.
|
|
1253
|
+
*/
|
|
1254
|
+
forkTitleHints = /* @__PURE__ */ new Map();
|
|
1255
|
+
forkTitleInFlight = /* @__PURE__ */ new Set();
|
|
1256
|
+
/** Subscribe to catalog invalidations ($events arms + socket reset). */
|
|
1257
|
+
onCatalogInvalidated(listener) {
|
|
1258
|
+
this.catalogInvalidated.add(listener);
|
|
1259
|
+
return () => this.catalogInvalidated.delete(listener);
|
|
1260
|
+
}
|
|
1261
|
+
fireCatalogInvalidated(scope) {
|
|
1262
|
+
for (const listener of this.catalogInvalidated) listener(scope);
|
|
1263
|
+
}
|
|
1264
|
+
/** approval/requested | question/requested → registry (idempotent by rpcId). */
|
|
1265
|
+
registerPending(frame, rpcId) {
|
|
1266
|
+
const sessionId = typeof frame.sessionId === "string" ? frame.sessionId : "";
|
|
1267
|
+
if (!sessionId) {
|
|
1268
|
+
this.conn.log("dsh-connection: answerable frame without sessionId — ignored", frame?.type);
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
if (typeof rpcId !== "string" || rpcId.length === 0) {
|
|
1272
|
+
this.conn.log("dsh-connection: answerable frame without rpcId — ignored", frame?.type);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
const kind = frame.type === "approval/requested" ? "approval" : "question";
|
|
1276
|
+
this.pendingAnswers.set(rpcId, {
|
|
1277
|
+
rpcId,
|
|
1278
|
+
sessionId,
|
|
1279
|
+
kind,
|
|
1280
|
+
body: frame,
|
|
1281
|
+
receivedAt: Date.now(),
|
|
1282
|
+
deliveredGen: this.conn.downlinks.generation
|
|
1283
|
+
});
|
|
1284
|
+
this.conn.log(`dsh-connection: pending ${kind} registered (${rpcId}) for ${sessionId}`);
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Withdraw one pending answer (0.1.2 $events settle/cancel): remove the
|
|
1288
|
+
* registry entry and record the outcome in the settlement ring. Unknown
|
|
1289
|
+
* ids settle nothing (answered before we subscribed — honest no-op).
|
|
1290
|
+
*/
|
|
1291
|
+
withdrawPending(eventId, outcome) {
|
|
1292
|
+
const pending = this.pendingAnswers.get(eventId);
|
|
1293
|
+
if (pending === void 0) {
|
|
1294
|
+
this.conn.log(`dsh-connection: settlement with no matching pending (${eventId}) — ignored`);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
this.pendingAnswers.delete(eventId);
|
|
1298
|
+
this.settledAnswers.set(eventId, {
|
|
1299
|
+
rpcId: eventId,
|
|
1300
|
+
sessionId: pending.sessionId,
|
|
1301
|
+
kind: pending.kind,
|
|
1302
|
+
outcome: outcome ?? "cancelled",
|
|
1303
|
+
settledAt: Date.now()
|
|
1304
|
+
});
|
|
1305
|
+
this.conn.log(`dsh-connection: ${pending.kind} settled (${eventId}) outcome=${outcome ?? "cancelled"}`);
|
|
1148
1306
|
}
|
|
1149
1307
|
/**
|
|
1150
1308
|
* Schedule the still-pending replay reap for this generation (ready
|
|
@@ -1163,8 +1321,8 @@ var DshConnection = class {
|
|
|
1163
1321
|
/** Withdraw every pending answer not re-delivered by the current generation. */
|
|
1164
1322
|
reapUnconfirmedPending() {
|
|
1165
1323
|
for (const [eventId, pending] of [...this.pendingAnswers]) {
|
|
1166
|
-
if (pending.deliveredGen >= this.generation) continue;
|
|
1167
|
-
this.log(`dsh-connection: reap unconfirmed pending (${eventId}) from generation ${pending.deliveredGen} — not in generation ${this.generation} replay`);
|
|
1324
|
+
if (pending.deliveredGen >= this.conn.downlinks.generation) continue;
|
|
1325
|
+
this.conn.log(`dsh-connection: reap unconfirmed pending (${eventId}) from generation ${pending.deliveredGen} — not in generation ${this.conn.downlinks.generation} replay`);
|
|
1168
1326
|
this.withdrawPending(eventId);
|
|
1169
1327
|
}
|
|
1170
1328
|
}
|
|
@@ -1182,7 +1340,7 @@ var DshConnection = class {
|
|
|
1182
1340
|
registerSettlement(frame) {
|
|
1183
1341
|
const sessionId = typeof frame.sessionId === "string" ? frame.sessionId : "";
|
|
1184
1342
|
if (!sessionId) {
|
|
1185
|
-
this.log("dsh-connection: settlement frame without sessionId — ignored", frame?.type);
|
|
1343
|
+
this.conn.log("dsh-connection: settlement frame without sessionId — ignored", frame?.type);
|
|
1186
1344
|
return;
|
|
1187
1345
|
}
|
|
1188
1346
|
const kind = frame.type === "approval/resolved" ? "approval" : "question";
|
|
@@ -1199,8 +1357,8 @@ var DshConnection = class {
|
|
|
1199
1357
|
outcome,
|
|
1200
1358
|
settledAt: Date.now()
|
|
1201
1359
|
});
|
|
1202
|
-
this.log(`dsh-connection: ${kind} settled (${rpcId}) outcome=${outcome}`);
|
|
1203
|
-
} else this.log(`dsh-connection: settlement with no matching pending (${frame?.type}) — ignored`);
|
|
1360
|
+
this.conn.log(`dsh-connection: ${kind} settled (${rpcId}) outcome=${outcome}`);
|
|
1361
|
+
} else this.conn.log(`dsh-connection: settlement with no matching pending (${frame?.type}) — ignored`);
|
|
1204
1362
|
}
|
|
1205
1363
|
/** Pending answers for a session (for the PollResponse extension). */
|
|
1206
1364
|
pendingFor(sessionId) {
|
|
@@ -1217,25 +1375,6 @@ var DshConnection = class {
|
|
|
1217
1375
|
if (s && s.sessionId === sessionId) this.settledAnswers.delete(id);
|
|
1218
1376
|
}
|
|
1219
1377
|
}
|
|
1220
|
-
/**
|
|
1221
|
-
* Fork-child display titles healed off the wire (2026-09-14, host gap
|
|
1222
|
-
* DSI-side): the host's session.list serves NO projections column for a
|
|
1223
|
-
* cold seeded (fork child) row — session-controller list.ts
|
|
1224
|
-
* projectionsFor short-circuits isSeeded headers because the projection
|
|
1225
|
-
* cache is keyed by the inherited cut a listed header does not carry.
|
|
1226
|
-
* The real title (what the panel shows once the session attaches) lives
|
|
1227
|
-
* in the child's ledger: a child that ran its own turns carries a NEW
|
|
1228
|
-
* session/title event near the tail, so ONE bounded tail-page read per
|
|
1229
|
-
* untitled fork row recovers it. The result caches for the process
|
|
1230
|
-
* lifetime (a retitle lands through the normal rename/live paths, which
|
|
1231
|
-
* serve the title directly). The parent row's title is the FALLBACK for
|
|
1232
|
-
* children the tail page shows never retitled — the inherited title IS
|
|
1233
|
-
* the parent's at fork time; stale only when the parent was renamed
|
|
1234
|
-
* after the fork. Absent hints and absent parents leave the row
|
|
1235
|
-
* honestly untitled.
|
|
1236
|
-
*/
|
|
1237
|
-
forkTitleHints = /* @__PURE__ */ new Map();
|
|
1238
|
-
forkTitleInFlight = /* @__PURE__ */ new Set();
|
|
1239
1378
|
async listSessions() {
|
|
1240
1379
|
const items = (await this.rawListSessions()).map(normalizeSessionRow);
|
|
1241
1380
|
const titleById = new Map(items.map((s) => [s.sessionId, s.title]));
|
|
@@ -1272,10 +1411,10 @@ var DshConnection = class {
|
|
|
1272
1411
|
*/
|
|
1273
1412
|
async healForkTitle(sessionId) {
|
|
1274
1413
|
try {
|
|
1275
|
-
const title = (await this.waitForProjections(sessionId))?.values?.["title"];
|
|
1414
|
+
const title = (await this.conn.downlinks.waitForProjections(sessionId))?.values?.["title"];
|
|
1276
1415
|
if (typeof title === "string" && title.length > 0) this.forkTitleHints.set(sessionId, title);
|
|
1277
1416
|
} catch (error) {
|
|
1278
|
-
this.log("dsh-connection: fork-title follow-baseline read failed for " + sessionId, error);
|
|
1417
|
+
this.conn.log("dsh-connection: fork-title follow-baseline read failed for " + sessionId, error);
|
|
1279
1418
|
} finally {
|
|
1280
1419
|
this.forkTitleInFlight.delete(sessionId);
|
|
1281
1420
|
}
|
|
@@ -1303,7 +1442,7 @@ var DshConnection = class {
|
|
|
1303
1442
|
}
|
|
1304
1443
|
/** Raw session/list rows (0.1.2) — projections.asOfSeq drives page calls. */
|
|
1305
1444
|
async rawListSessions() {
|
|
1306
|
-
const raw = await this.rpc(DSH_METHODS.list, DSH_ARGS.list());
|
|
1445
|
+
const raw = await this.conn.rpc(DSH_METHODS.list, DSH_ARGS.list());
|
|
1307
1446
|
const rows = Array.isArray(raw?.items) ? raw.items : [];
|
|
1308
1447
|
for (const row of rows) this.foldSubagentHint(row);
|
|
1309
1448
|
return rows;
|
|
@@ -1371,7 +1510,7 @@ var DshConnection = class {
|
|
|
1371
1510
|
* "no served cursor", not "below the ledger head".
|
|
1372
1511
|
*/
|
|
1373
1512
|
async pageCursorFor(sessionId) {
|
|
1374
|
-
const live = this.bufferFor(sessionId).lastSeq;
|
|
1513
|
+
const live = this.conn.sessions.bufferFor(sessionId).lastSeq;
|
|
1375
1514
|
if (live >= 0) return live;
|
|
1376
1515
|
const throughSeq = (await this.rawListSessions()).find((r) => r.sessionId === sessionId)?.projections?.asOfSeq;
|
|
1377
1516
|
if (typeof throughSeq === "number" && throughSeq >= 0) return throughSeq;
|
|
@@ -1385,7 +1524,7 @@ var DshConnection = class {
|
|
|
1385
1524
|
*/
|
|
1386
1525
|
async history(sessionId) {
|
|
1387
1526
|
const throughSeq = await this.pageCursorFor(sessionId);
|
|
1388
|
-
return this.pageAsHistory(await this.rpc(DSH_METHODS.page, DSH_ARGS.page({
|
|
1527
|
+
return this.pageAsHistory(await this.conn.rpc(DSH_METHODS.page, DSH_ARGS.page({
|
|
1389
1528
|
address: this.addressFor(sessionId),
|
|
1390
1529
|
throughSeq
|
|
1391
1530
|
})));
|
|
@@ -1394,37 +1533,21 @@ var DshConnection = class {
|
|
|
1394
1533
|
* workspace button's host call. Raw view passthrough — DSI renders
|
|
1395
1534
|
* only title/path; ordering and session membership stay host-owned. */
|
|
1396
1535
|
async createWorkspace(path) {
|
|
1397
|
-
return await this.rpc(DSH_METHODS.workspaceCreate, DSH_ARGS.workspaceCreate({ path }));
|
|
1536
|
+
return await this.conn.rpc(DSH_METHODS.workspaceCreate, DSH_ARGS.workspaceCreate({ path }));
|
|
1398
1537
|
}
|
|
1399
1538
|
/** Workspace rename (Chip Menu ADR D3): {request:{workspaceId,title}}
|
|
1400
1539
|
* → the updated Workspace view. Refusals throw DshRpcError
|
|
1401
1540
|
* (workspace/not-found, workspace/name-conflict). No cache mutation —
|
|
1402
1541
|
* the workspace/follow upsert frame owns the registry (ADR D6). */
|
|
1403
1542
|
async renameWorkspace(workspaceId, title) {
|
|
1404
|
-
return await this.rpc(DSH_METHODS.workspaceRename, DSH_ARGS.workspaceRename(workspaceId, title));
|
|
1543
|
+
return await this.conn.rpc(DSH_METHODS.workspaceRename, DSH_ARGS.workspaceRename(workspaceId, title));
|
|
1405
1544
|
}
|
|
1406
1545
|
/** Workspace delete (Chip Menu ADR D3): {request:{workspaceId}} → the
|
|
1407
1546
|
* removed id. The host removes ONLY the registry row (sessions survive,
|
|
1408
1547
|
* ungrouped); the workspace/follow remove frame owns the registry
|
|
1409
1548
|
* (ADR D6) — no local mutation here. */
|
|
1410
1549
|
async deleteWorkspace(workspaceId) {
|
|
1411
|
-
return await this.rpc(DSH_METHODS.workspaceDelete, DSH_ARGS.workspaceDelete(workspaceId));
|
|
1412
|
-
}
|
|
1413
|
-
/** Goal Bar (2026-09-08, ADR amendment): one goal mutation through the
|
|
1414
|
-
* host's Goal Remotes — goals/pause | goals/resume | goals/clear |
|
|
1415
|
-
* goals/edit with the CAS ref {id, revision} (a stale revision loses
|
|
1416
|
-
* host-side). goals/edit additionally carries the changed-fields
|
|
1417
|
-
* request {objective?, maxGoalRounds?} (Goal Editor, 2026-09-09). The
|
|
1418
|
-
* raw RemoteResult passes through; the route maps refusals. */
|
|
1419
|
-
async goalVerb(verb, sessionId, ref, request) {
|
|
1420
|
-
return this.rpc(`goals/${verb}`, request === void 0 ? {
|
|
1421
|
-
agentId: sessionId,
|
|
1422
|
-
ref
|
|
1423
|
-
} : {
|
|
1424
|
-
agentId: sessionId,
|
|
1425
|
-
ref,
|
|
1426
|
-
request
|
|
1427
|
-
});
|
|
1550
|
+
return await this.conn.rpc(DSH_METHODS.workspaceDelete, DSH_ARGS.workspaceDelete(workspaceId));
|
|
1428
1551
|
}
|
|
1429
1552
|
/**
|
|
1430
1553
|
* workspaceFiles/read — one page of a UTF-8 text file inside the
|
|
@@ -1440,22 +1563,22 @@ var DshConnection = class {
|
|
|
1440
1563
|
* @param range - line window; omitted fields take the host's page defaults.
|
|
1441
1564
|
*/
|
|
1442
1565
|
async readWorkspaceFile(sessionId, path, range) {
|
|
1443
|
-
return await this.rpc(DSH_METHODS.workspaceFileRead, DSH_ARGS.workspaceFileRead(sessionId, path, range));
|
|
1566
|
+
return await this.conn.rpc(DSH_METHODS.workspaceFileRead, DSH_ARGS.workspaceFileRead(sessionId, path, range));
|
|
1444
1567
|
}
|
|
1445
1568
|
/**
|
|
1446
1569
|
* workspaceFiles/readBytes — one byte window of a workspace file, raw
|
|
1447
1570
|
* bytes (base64 receipt), no text decoding and no binary refusal
|
|
1448
|
-
* (2026-09-10: binary previews such as images read through this;
|
|
1571
|
+
* (2026-09-10: binary previews such as images read through this; read
|
|
1449
1572
|
* refuses them workspace-file/not-text).
|
|
1450
1573
|
*
|
|
1451
1574
|
* @param sessionId - owning session; the host confines the read to its
|
|
1452
|
-
workspace root by containment.
|
|
1575
|
+
* workspace root by containment.
|
|
1453
1576
|
* @param path - workspace path, absolute or root-relative.
|
|
1454
1577
|
* @param range - byte window {offset, length}; omitted fields take the
|
|
1455
|
-
host's window defaults.
|
|
1578
|
+
* host's window defaults.
|
|
1456
1579
|
*/
|
|
1457
1580
|
async readWorkspaceFileBytes(sessionId, path, range) {
|
|
1458
|
-
return await this.rpc(DSH_METHODS.workspaceFileReadBytes, DSH_ARGS.workspaceFileReadBytes(sessionId, path, range));
|
|
1581
|
+
return await this.conn.rpc(DSH_METHODS.workspaceFileReadBytes, DSH_ARGS.workspaceFileReadBytes(sessionId, path, range));
|
|
1459
1582
|
}
|
|
1460
1583
|
/**
|
|
1461
1584
|
* workspaceFiles/list — one directory level inside the session's
|
|
@@ -1465,15 +1588,7 @@ var DshConnection = class {
|
|
|
1465
1588
|
* {workspaceFileScopeId, path}; path is workspace-relative, '' = the root.
|
|
1466
1589
|
*/
|
|
1467
1590
|
async listWorkspaceDirectory(sessionId, path) {
|
|
1468
|
-
return await this.rpc(DSH_METHODS.workspaceFileList, DSH_ARGS.workspaceFileList(sessionId, path));
|
|
1469
|
-
}
|
|
1470
|
-
/** The workspace registry (2026-08-23 registry parity) — the AUTHORITY
|
|
1471
|
-
* for DSI's workspace pills/chips: a deleted workspace disappears, and
|
|
1472
|
-
* sessions outside every registry entry render cue-less (DSH's
|
|
1473
|
-
* Ungrouped). Membership is sessionIds, not cwd matching. */
|
|
1474
|
-
async listWorkspaces() {
|
|
1475
|
-
if (!await this.awaitWorkspaceBaseline()) this.log("dsh-connection: workspace baseline did not land in time — honest empty");
|
|
1476
|
-
return { items: [...this.workspaces] };
|
|
1591
|
+
return await this.conn.rpc(DSH_METHODS.workspaceFileList, DSH_ARGS.workspaceFileList(sessionId, path));
|
|
1477
1592
|
}
|
|
1478
1593
|
/**
|
|
1479
1594
|
* host.listDirectory — one browsed directory level for the Add-workspace
|
|
@@ -1482,7 +1597,7 @@ var DshConnection = class {
|
|
|
1482
1597
|
* garbage rows drop out, never throw, never invent data.
|
|
1483
1598
|
*/
|
|
1484
1599
|
async listDirectory(path) {
|
|
1485
|
-
const raw = await this.rpc(DSH_METHODS.listDirectory, DSH_ARGS.listDirectory(path));
|
|
1600
|
+
const raw = await this.conn.rpc(DSH_METHODS.listDirectory, DSH_ARGS.listDirectory(path));
|
|
1486
1601
|
const entry = (v) => {
|
|
1487
1602
|
if (v === null || typeof v !== "object") return null;
|
|
1488
1603
|
const e = v;
|
|
@@ -1521,13 +1636,13 @@ var DshConnection = class {
|
|
|
1521
1636
|
* @param attachmentId - durable content-addressed id from an ImageBlock.
|
|
1522
1637
|
*/
|
|
1523
1638
|
async readAttachment(sessionId, attachmentId) {
|
|
1524
|
-
return await this.rpc(DSH_METHODS.attachment, DSH_ARGS.attachment({
|
|
1639
|
+
return await this.conn.rpc(DSH_METHODS.attachment, DSH_ARGS.attachment({
|
|
1525
1640
|
sessionId,
|
|
1526
1641
|
attachmentId
|
|
1527
1642
|
}));
|
|
1528
1643
|
}
|
|
1529
1644
|
async pickDirectory(signal) {
|
|
1530
|
-
const raw = await this.rpc(DSH_METHODS.pickDirectory, DSH_ARGS.pickDirectory(), signal);
|
|
1645
|
+
const raw = await this.conn.rpc(DSH_METHODS.pickDirectory, DSH_ARGS.pickDirectory(), signal);
|
|
1531
1646
|
return { path: typeof raw === "string" && raw.length > 0 ? raw : null };
|
|
1532
1647
|
}
|
|
1533
1648
|
/**
|
|
@@ -1538,7 +1653,7 @@ var DshConnection = class {
|
|
|
1538
1653
|
*/
|
|
1539
1654
|
async historyPage(sessionId, beforeSeq) {
|
|
1540
1655
|
const throughSeq = await this.pageCursorFor(sessionId);
|
|
1541
|
-
return this.pageAsHistory(await this.rpc(DSH_METHODS.page, DSH_ARGS.page({
|
|
1656
|
+
return this.pageAsHistory(await this.conn.rpc(DSH_METHODS.page, DSH_ARGS.page({
|
|
1542
1657
|
address: this.addressFor(sessionId),
|
|
1543
1658
|
throughSeq,
|
|
1544
1659
|
beforeSeq
|
|
@@ -1558,7 +1673,7 @@ var DshConnection = class {
|
|
|
1558
1673
|
* Live-probed round-trip 2026-08-21 (probe7 → real session id).
|
|
1559
1674
|
*/
|
|
1560
1675
|
async createSession(cwd, agentPreset) {
|
|
1561
|
-
return await this.rpc(DSH_METHODS.create, DSH_ARGS.create({
|
|
1676
|
+
return await this.conn.rpc(DSH_METHODS.create, DSH_ARGS.create({
|
|
1562
1677
|
...cwd === void 0 ? {} : { cwd },
|
|
1563
1678
|
...agentPreset === void 0 ? {} : { agentPreset }
|
|
1564
1679
|
}));
|
|
@@ -1571,7 +1686,7 @@ var DshConnection = class {
|
|
|
1571
1686
|
* contract and refusals).
|
|
1572
1687
|
*/
|
|
1573
1688
|
async forkSession(sessionId, atSeq) {
|
|
1574
|
-
return await this.rpc(DSH_METHODS.fork, DSH_ARGS.fork({
|
|
1689
|
+
return await this.conn.rpc(DSH_METHODS.fork, DSH_ARGS.fork({
|
|
1575
1690
|
sessionId,
|
|
1576
1691
|
...atSeq === void 0 ? {} : { atSeq }
|
|
1577
1692
|
}));
|
|
@@ -1585,7 +1700,7 @@ var DshConnection = class {
|
|
|
1585
1700
|
*/
|
|
1586
1701
|
async listPresets() {
|
|
1587
1702
|
const overlayEnglish = readDshPresetEnglishConfig().presetEnglish;
|
|
1588
|
-
const raw = await this.rpc(DSH_METHODS.presets, DSH_ARGS.presets());
|
|
1703
|
+
const raw = await this.conn.rpc(DSH_METHODS.presets, DSH_ARGS.presets());
|
|
1589
1704
|
return { presets: (Array.isArray(raw?.presets) ? raw.presets : []).map((row) => normalizePreset(row, overlayEnglish)) };
|
|
1590
1705
|
}
|
|
1591
1706
|
/**
|
|
@@ -1600,8 +1715,8 @@ var DshConnection = class {
|
|
|
1600
1715
|
type: "text",
|
|
1601
1716
|
text: payload
|
|
1602
1717
|
}] : payload;
|
|
1603
|
-
return await this.rpc(DSH_METHODS.prompt, DSH_ARGS.prompt({
|
|
1604
|
-
requestId: this.rpcIdFactory(),
|
|
1718
|
+
return await this.conn.rpc(DSH_METHODS.prompt, DSH_ARGS.prompt({
|
|
1719
|
+
requestId: this.conn.rpcIdFactory(),
|
|
1605
1720
|
sessionId,
|
|
1606
1721
|
mode,
|
|
1607
1722
|
content,
|
|
@@ -1619,7 +1734,7 @@ var DshConnection = class {
|
|
|
1619
1734
|
* receipt callers must distinguish from a success.
|
|
1620
1735
|
*/
|
|
1621
1736
|
async executeCommand(sessionId, line) {
|
|
1622
|
-
return await this.rpc(DSH_METHODS.commandExecute, DSH_ARGS.commandExecute(sessionId, line, [])) ?? null;
|
|
1737
|
+
return await this.conn.rpc(DSH_METHODS.commandExecute, DSH_ARGS.commandExecute(sessionId, line, [])) ?? null;
|
|
1623
1738
|
}
|
|
1624
1739
|
/**
|
|
1625
1740
|
* commands/list (Slash Menu W1, task 1.2) — the host's command catalog,
|
|
@@ -1629,7 +1744,7 @@ var DshConnection = class {
|
|
|
1629
1744
|
* open, never on panel mount (ADR §3.3).
|
|
1630
1745
|
*/
|
|
1631
1746
|
async listCommands(sessionId) {
|
|
1632
|
-
return await this.rpc(DSH_METHODS.commandsList, DSH_ARGS.commandsList(sessionId));
|
|
1747
|
+
return await this.conn.rpc(DSH_METHODS.commandsList, DSH_ARGS.commandsList(sessionId));
|
|
1633
1748
|
}
|
|
1634
1749
|
/**
|
|
1635
1750
|
* skills/list (Slash Menu W1, task 1.2) — the session's user-invocable
|
|
@@ -1638,11 +1753,11 @@ var DshConnection = class {
|
|
|
1638
1753
|
* read it freely on first open.
|
|
1639
1754
|
*/
|
|
1640
1755
|
async listSkills(sessionId) {
|
|
1641
|
-
return await this.rpc(DSH_METHODS.skillsList, DSH_ARGS.skillsList(sessionId));
|
|
1756
|
+
return await this.conn.rpc(DSH_METHODS.skillsList, DSH_ARGS.skillsList(sessionId));
|
|
1642
1757
|
}
|
|
1643
1758
|
/** session.cancel */
|
|
1644
1759
|
async cancel(sessionId) {
|
|
1645
|
-
return await this.rpc(DSH_METHODS.cancel, DSH_ARGS.cancel({ sessionId }));
|
|
1760
|
+
return await this.conn.rpc(DSH_METHODS.cancel, DSH_ARGS.cancel({ sessionId }));
|
|
1646
1761
|
}
|
|
1647
1762
|
/**
|
|
1648
1763
|
* POC-3 W3 (3.1) — session.rename {sessionId, title}.
|
|
@@ -1653,7 +1768,7 @@ var DshConnection = class {
|
|
|
1653
1768
|
* RpcError → 502 upstream). Session-backed subagents reject `agent-busy`.
|
|
1654
1769
|
*/
|
|
1655
1770
|
async renameSession(sessionId, title) {
|
|
1656
|
-
return await this.rpc(DSH_METHODS.rename, DSH_ARGS.rename({
|
|
1771
|
+
return await this.conn.rpc(DSH_METHODS.rename, DSH_ARGS.rename({
|
|
1657
1772
|
sessionId,
|
|
1658
1773
|
title
|
|
1659
1774
|
}));
|
|
@@ -1667,7 +1782,7 @@ var DshConnection = class {
|
|
|
1667
1782
|
* below never throws (task 3.2-T: garbage catalog → empty, not an error).
|
|
1668
1783
|
*/
|
|
1669
1784
|
async listModels(_sessionId) {
|
|
1670
|
-
return normalizeModelDirectory(await this.rpc(DSH_METHODS.modelCatalog, DSH_ARGS.modelCatalog()));
|
|
1785
|
+
return normalizeModelDirectory(await this.conn.rpc(DSH_METHODS.modelCatalog, DSH_ARGS.modelCatalog()));
|
|
1671
1786
|
}
|
|
1672
1787
|
/**
|
|
1673
1788
|
* POC-3 W3 (3.2) — session.selectModel {sessionId, provider, model,
|
|
@@ -1676,7 +1791,7 @@ var DshConnection = class {
|
|
|
1676
1791
|
* effort. Subagents reject `session/agent-busy` (RpcError → 502 upstream).
|
|
1677
1792
|
*/
|
|
1678
1793
|
async selectModel(sessionId, provider, model, reasoningEffort) {
|
|
1679
|
-
return normalizeModelSelection((await this.rpc(DSH_METHODS.selectModel, DSH_ARGS.selectModel({
|
|
1794
|
+
return normalizeModelSelection((await this.conn.rpc(DSH_METHODS.selectModel, DSH_ARGS.selectModel({
|
|
1680
1795
|
sessionId,
|
|
1681
1796
|
provider,
|
|
1682
1797
|
model,
|
|
@@ -1705,7 +1820,7 @@ var DshConnection = class {
|
|
|
1705
1820
|
* the wire carries the host's contract, not the panel's convenience.
|
|
1706
1821
|
*/
|
|
1707
1822
|
async respond(rpcId, payload) {
|
|
1708
|
-
if (this.eventClientId === null) throw new Error("dsh-connection: no $events generation to answer through yet");
|
|
1823
|
+
if (this.conn.downlinks.eventClientId === null) throw new Error("dsh-connection: no $events generation to answer through yet");
|
|
1709
1824
|
const { sessionId, ...rest } = payload;
|
|
1710
1825
|
const value = rest.outcome !== void 0 ? rest.outcome : rest.answer !== void 0 ? rest.answer : rest.answers !== void 0 ? { answers: rest.answers } : rest;
|
|
1711
1826
|
try {
|
|
@@ -1717,7 +1832,7 @@ var DshConnection = class {
|
|
|
1717
1832
|
reason: "not-pending"
|
|
1718
1833
|
};
|
|
1719
1834
|
if (err instanceof DshRpcError && err.code === "gateway/internal" && err.message.includes("no active event stream")) {
|
|
1720
|
-
const previous = this.eventClientId;
|
|
1835
|
+
const previous = this.conn.downlinks.eventClientId;
|
|
1721
1836
|
if (previous !== null && await this.refreshEventGeneration(previous)) {
|
|
1722
1837
|
await this.sendEventResult(rpcId, value);
|
|
1723
1838
|
this.forgetAnswered(rpcId, value);
|
|
@@ -1736,8 +1851,8 @@ var DshConnection = class {
|
|
|
1736
1851
|
*/
|
|
1737
1852
|
async fetchHostPath(path, signal) {
|
|
1738
1853
|
const attempt = async () => {
|
|
1739
|
-
const cookie = await this.auth.ensureCookie();
|
|
1740
|
-
return this.fetchFn(`${this.baseUrl}${path}`, {
|
|
1854
|
+
const cookie = await this.conn.auth.ensureCookie();
|
|
1855
|
+
return this.conn.fetchFn(`${this.conn.baseUrl}${path}`, {
|
|
1741
1856
|
method: "GET",
|
|
1742
1857
|
headers: cookie === null ? {} : { cookie },
|
|
1743
1858
|
...signal !== void 0 ? { signal } : {}
|
|
@@ -1745,15 +1860,15 @@ var DshConnection = class {
|
|
|
1745
1860
|
};
|
|
1746
1861
|
let response = await attempt();
|
|
1747
1862
|
if (response.status === 401) {
|
|
1748
|
-
this.auth.invalidate();
|
|
1863
|
+
this.conn.auth.invalidate();
|
|
1749
1864
|
response = await attempt();
|
|
1750
1865
|
}
|
|
1751
1866
|
return response;
|
|
1752
1867
|
}
|
|
1753
1868
|
/** One $events/result answer under the CURRENT generation's clientId. */
|
|
1754
1869
|
sendEventResult(rpcId, value) {
|
|
1755
|
-
return this.rpc(REMOTE_EVENT_RESULT_ENDPOINT, {
|
|
1756
|
-
clientId: this.eventClientId,
|
|
1870
|
+
return this.conn.rpc(REMOTE_EVENT_RESULT_ENDPOINT, {
|
|
1871
|
+
clientId: this.conn.downlinks.eventClientId,
|
|
1757
1872
|
eventId: rpcId,
|
|
1758
1873
|
outcome: {
|
|
1759
1874
|
kind: "result",
|
|
@@ -1768,16 +1883,13 @@ var DshConnection = class {
|
|
|
1768
1883
|
* caller may retry an answer against it.
|
|
1769
1884
|
*/
|
|
1770
1885
|
async refreshEventGeneration(previousClientId) {
|
|
1771
|
-
this.log("dsh-connection: $events generation stale — rebuilding for the answer retry");
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
}
|
|
1776
|
-
this.teardownSockets();
|
|
1777
|
-
this.ensureDownlinks();
|
|
1886
|
+
this.conn.log("dsh-connection: $events generation stale — rebuilding for the answer retry");
|
|
1887
|
+
this.conn.downlinks.cancelReconnect();
|
|
1888
|
+
this.conn.downlinks.teardownSockets();
|
|
1889
|
+
this.conn.downlinks.ensureDownlinks();
|
|
1778
1890
|
const deadline = Date.now() + this.staleGenerationRetryMs;
|
|
1779
|
-
while (!this.disposed && this.eventClientId === previousClientId && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1780
|
-
return this.eventClientId !== previousClientId && this.eventClientId !== null;
|
|
1891
|
+
while (!this.conn.disposed && this.conn.downlinks.eventClientId === previousClientId && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1892
|
+
return this.conn.downlinks.eventClientId !== previousClientId && this.conn.downlinks.eventClientId !== null;
|
|
1781
1893
|
}
|
|
1782
1894
|
/**
|
|
1783
1895
|
* Resync: replace the ring buffer with the ledger's tail page (BC-4).
|
|
@@ -1785,12 +1897,166 @@ var DshConnection = class {
|
|
|
1785
1897
|
*/
|
|
1786
1898
|
async resyncFromLedger(sessionId) {
|
|
1787
1899
|
const events = ((await this.history(sessionId)).events ?? []).map((h) => h.event);
|
|
1788
|
-
const buf = this.bufferFor(sessionId);
|
|
1789
|
-
buf.entries = [...events].slice(-ringCapacity());
|
|
1900
|
+
const buf = this.conn.sessions.bufferFor(sessionId);
|
|
1901
|
+
buf.entries = [...events].slice(-this.ringCapacity());
|
|
1790
1902
|
buf.lastSeq = events.length > 0 ? events[events.length - 1].seq : -1;
|
|
1791
1903
|
buf.coverageFloor = buf.entries[0]?.seq ?? buf.lastSeq;
|
|
1792
1904
|
return buf.entries;
|
|
1793
1905
|
}
|
|
1906
|
+
/** Teardown hook: transient registries never leak entries. */
|
|
1907
|
+
dispose() {
|
|
1908
|
+
clearTimeout(this.answerReapTimer);
|
|
1909
|
+
this.pendingAnswers.clear();
|
|
1910
|
+
this.settledAnswers.clear();
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
//#endregion
|
|
1914
|
+
//#region src/lib/server/dsh-connection.ts
|
|
1915
|
+
/**
|
|
1916
|
+
* dsh-connection — the transport singleton (BC-5) owning every wire byte to
|
|
1917
|
+
* the DSH Host (BC-6: it speaks only through dsh-rpc helpers and the two
|
|
1918
|
+
* downlink WebSocket paths).
|
|
1919
|
+
*
|
|
1920
|
+
* State model (PRD §3.5):
|
|
1921
|
+
* mux frames (session/subscribed, session/event) → per-session ring buffer
|
|
1922
|
+
* host frames (host/session-status) → per-session running map
|
|
1923
|
+
* running map re-baselines from session.list at every generation open
|
|
1924
|
+
* (2026-08-26): the host pushes status only on TRANSITIONS and sends no
|
|
1925
|
+
* initial snapshot, so a downlink rebuilt mid-turn would otherwise never
|
|
1926
|
+
* learn the session was already running.
|
|
1927
|
+
* ring buffer = freshness only; the ledger (session.history) is the truth —
|
|
1928
|
+
* cold loads and every resync re-read the ledger (BC-4).
|
|
1929
|
+
*
|
|
1930
|
+
* Generation semantics (guide Part 1): the two downlinks form one generation;
|
|
1931
|
+
* if either dies, both are rebuilt after backoff.
|
|
1932
|
+
*/
|
|
1933
|
+
/** Live capacity from ~/.dsi/settings.yaml (no cache — runtime edits
|
|
1934
|
+
* apply, same contract as every other config section). */
|
|
1935
|
+
function ringCapacity() {
|
|
1936
|
+
return readServerConfig().ringCapacity;
|
|
1937
|
+
}
|
|
1938
|
+
var singleton;
|
|
1939
|
+
/** The process-wide transport (BC-5). Creates lazily on first use. */
|
|
1940
|
+
function getDshConnection(opts = {}) {
|
|
1941
|
+
singleton ??= new DshConnection(opts);
|
|
1942
|
+
return singleton;
|
|
1943
|
+
}
|
|
1944
|
+
var DshConnection = class {
|
|
1945
|
+
baseUrl;
|
|
1946
|
+
fetchFn;
|
|
1947
|
+
wsFactory;
|
|
1948
|
+
auth;
|
|
1949
|
+
rpcIdFactory;
|
|
1950
|
+
backoff;
|
|
1951
|
+
log;
|
|
1952
|
+
/**
|
|
1953
|
+
* ADR-0022 W2 (move-whole): the session state machines — ring buffers,
|
|
1954
|
+
* running map + seed races, live assistant-stream tails — live in
|
|
1955
|
+
* connection/session-state.ts; the shell delegates.
|
|
1956
|
+
*/
|
|
1957
|
+
sessions;
|
|
1958
|
+
/** ADR-0022 W3 (move-whole): the downlink generation lifecycle + frame
|
|
1959
|
+
* demux live in connection/downlinks.ts; the shell delegates. */
|
|
1960
|
+
downlinks;
|
|
1961
|
+
/** ADR-0022 W4 (move-whole): the RPC relay + answer registry live in
|
|
1962
|
+
* connection/rpc-relay.ts; the shell delegates. */
|
|
1963
|
+
relay;
|
|
1964
|
+
disposed = false;
|
|
1965
|
+
constructor(opts = {}) {
|
|
1966
|
+
this.baseUrl = opts.baseUrl ?? dshBaseUrl();
|
|
1967
|
+
this.fetchFn = opts.fetchFn ?? fetch;
|
|
1968
|
+
this.wsFactory = opts.wsFactory ?? ((url, headers) => new WebSocket(url, headers === void 0 ? void 0 : { headers }));
|
|
1969
|
+
this.auth = opts.auth ?? new DshAuth({
|
|
1970
|
+
baseUrl: this.baseUrl,
|
|
1971
|
+
log: opts.log
|
|
1972
|
+
});
|
|
1973
|
+
this.rpcIdFactory = opts.rpcIdFactory ?? (() => crypto.randomUUID());
|
|
1974
|
+
this.backoff = opts.backoffScheduleMs ?? [
|
|
1975
|
+
1e3,
|
|
1976
|
+
2e3,
|
|
1977
|
+
5e3,
|
|
1978
|
+
1e4,
|
|
1979
|
+
3e4
|
|
1980
|
+
];
|
|
1981
|
+
const answerReapGraceMs = opts.answerReapGraceMs ?? 3e3;
|
|
1982
|
+
const staleGenerationRetryMs = opts.staleGenerationRetryMs ?? 5e3;
|
|
1983
|
+
this.log = opts.log ?? (() => {});
|
|
1984
|
+
this.sessions = new SessionStateStore({
|
|
1985
|
+
log: this.log,
|
|
1986
|
+
ringCapacity,
|
|
1987
|
+
listSessions: () => this.listSessions(),
|
|
1988
|
+
generation: () => this.downlinks.generation,
|
|
1989
|
+
disposed: () => this.disposed
|
|
1990
|
+
});
|
|
1991
|
+
this.downlinks = new DownlinkManager(this);
|
|
1992
|
+
this.relay = new RpcRelay(this, ringCapacity, {
|
|
1993
|
+
answerReapGraceMs,
|
|
1994
|
+
staleGenerationRetryMs
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
/** Open the stream generation (idempotent) — see DownlinkManager. */
|
|
1998
|
+
ensureDownlinks() {
|
|
1999
|
+
this.downlinks.ensureDownlinks();
|
|
2000
|
+
}
|
|
2001
|
+
/** Subscribe one session (0.1.2) — see DownlinkManager. */
|
|
2002
|
+
subscribe(sessionId) {
|
|
2003
|
+
this.downlinks.subscribe(sessionId);
|
|
2004
|
+
}
|
|
2005
|
+
/** Per-session projection value (W2) — see DownlinkManager. */
|
|
2006
|
+
projectionValue(sessionId, key) {
|
|
2007
|
+
return this.downlinks.projectionValue(sessionId, key);
|
|
2008
|
+
}
|
|
2009
|
+
/** The whole stored block for one session (null when absent). */
|
|
2010
|
+
projectionBlock(sessionId) {
|
|
2011
|
+
return this.downlinks.projectionBlock(sessionId);
|
|
2012
|
+
}
|
|
2013
|
+
/** Bounded wait for the session's FIRST projection baseline. */
|
|
2014
|
+
async waitForProjections(sessionId, timeoutMs = 2500) {
|
|
2015
|
+
return this.downlinks.waitForProjections(sessionId, timeoutMs);
|
|
2016
|
+
}
|
|
2017
|
+
/** The workspace registry — the AUTHORITY for DSI's workspace pills/chips. */
|
|
2018
|
+
async listWorkspaces() {
|
|
2019
|
+
return this.downlinks.listWorkspaces();
|
|
2020
|
+
}
|
|
2021
|
+
onMuxFrame(payload, rpcId) {
|
|
2022
|
+
this.downlinks.reconnectAttempt = 0;
|
|
2023
|
+
const frame = payload;
|
|
2024
|
+
switch (frame?.type) {
|
|
2025
|
+
case "session/subscribed":
|
|
2026
|
+
if (typeof frame.sessionId === "string") this.sessions.seedBaseline(frame.sessionId, frame.lastSeq ?? -1);
|
|
2027
|
+
return;
|
|
2028
|
+
case "session/event":
|
|
2029
|
+
if (frame.sessionId && frame.event) this.sessions.appendEvent(frame.sessionId, frame.event);
|
|
2030
|
+
return;
|
|
2031
|
+
case "approval/requested":
|
|
2032
|
+
case "question/requested":
|
|
2033
|
+
this.relay.registerPending(frame, rpcId);
|
|
2034
|
+
return;
|
|
2035
|
+
case "approval/resolved":
|
|
2036
|
+
case "question/resolved":
|
|
2037
|
+
this.relay.registerSettlement(frame);
|
|
2038
|
+
return;
|
|
2039
|
+
default: this.log("dsh-connection: mux frame ignored", frame?.type);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
/** The session's live tail for the events poll, or null when no attempt
|
|
2043
|
+
* is in flight (0.1.3-alpha.1 live assistant-stream tail). */
|
|
2044
|
+
liveAssistantStream(sessionId) {
|
|
2045
|
+
return this.sessions.liveAssistantStream(sessionId);
|
|
2046
|
+
}
|
|
2047
|
+
/** Delta slice: events with seq > n, plus current lastSeq/running. Wave 4.2:
|
|
2048
|
+
* the gap flag is true when the caller's since mark fell into a hole the
|
|
2049
|
+
* buffer no longer covers (mux drop) — the response carries the resync flag. */
|
|
2050
|
+
eventsSince(sessionId, n) {
|
|
2051
|
+
return this.sessions.eventsSince(sessionId, n);
|
|
2052
|
+
}
|
|
2053
|
+
/** True when the session has a live baseline (subscribed at least once). */
|
|
2054
|
+
hasBaseline(sessionId) {
|
|
2055
|
+
return this.sessions.hasBaseline(sessionId);
|
|
2056
|
+
}
|
|
2057
|
+
isRunning(sessionId) {
|
|
2058
|
+
return this.sessions.isRunning(sessionId);
|
|
2059
|
+
}
|
|
1794
2060
|
async rpc(method, payload, signal) {
|
|
1795
2061
|
const body = JSON.stringify({
|
|
1796
2062
|
type: "client-request",
|
|
@@ -1820,220 +2086,161 @@ var DshConnection = class {
|
|
|
1820
2086
|
const rpcId = JSON.parse(body).rpcId;
|
|
1821
2087
|
return parseResponse(await response.text(), rpcId);
|
|
1822
2088
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
this.teardownSockets();
|
|
1827
|
-
this.pendingAnswers.clear();
|
|
1828
|
-
this.settledAnswers.clear();
|
|
1829
|
-
this.buffers.clear();
|
|
1830
|
-
this.liveTails.clear();
|
|
1831
|
-
this.running.clear();
|
|
2089
|
+
/** Subscribe to catalog invalidations ($events arms + socket reset). */
|
|
2090
|
+
onCatalogInvalidated(listener) {
|
|
2091
|
+
return this.relay.onCatalogInvalidated(listener);
|
|
1832
2092
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
return `follow-${sessionId}-${String(streamSeq)}`;
|
|
1837
|
-
}
|
|
1838
|
-
/**
|
|
1839
|
-
* Expand ONE follow/page event payload into its ring-buffer form
|
|
1840
|
-
* (single-element list — ledger v2 serves every record as a plain event).
|
|
1841
|
-
*/
|
|
1842
|
-
function expandEntry(e) {
|
|
1843
|
-
return [{
|
|
1844
|
-
type: e.type,
|
|
1845
|
-
seq: typeof e.seq === "number" ? e.seq : -1,
|
|
1846
|
-
time: typeof e.time === "number" ? e.time : 0,
|
|
1847
|
-
data: e.data
|
|
1848
|
-
}];
|
|
1849
|
-
}
|
|
1850
|
-
/** Expand one page/snapshot record ({type:'event', event}) into its member
|
|
1851
|
-
* events; a record without an event payload contributes nothing. */
|
|
1852
|
-
function expandRecord(record) {
|
|
1853
|
-
if (record === void 0 || record.event === void 0) return [];
|
|
1854
|
-
return expandEntry(record.event);
|
|
1855
|
-
}
|
|
1856
|
-
/** Expand a records array into a flat event list. */
|
|
1857
|
-
function expandRecords(records) {
|
|
1858
|
-
return records.flatMap((record) => expandRecord(record));
|
|
1859
|
-
}
|
|
1860
|
-
/** The last member time of one compact stream record, or undefined when
|
|
1861
|
-
* the record carries no readable time (malformed members stay silent —
|
|
1862
|
-
* the baseline is presentation seed data, never re-validated truth). */
|
|
1863
|
-
function compactRecordTailTime(record) {
|
|
1864
|
-
if (record.type === "chunk") return typeof record.time === "number" ? record.time : void 0;
|
|
1865
|
-
if (record.type === "text-chunks" || record.type === "reasoning-chunks" || record.type === "tool-call-chunks") {
|
|
1866
|
-
const time0 = typeof record.time0 === "number" ? record.time0 : void 0;
|
|
1867
|
-
const dt = Array.isArray(record.dt) ? record.dt : [];
|
|
1868
|
-
if (time0 === void 0 || dt.some((g) => typeof g !== "number")) return void 0;
|
|
1869
|
-
return dt.reduce((t, g) => t + g, time0);
|
|
2093
|
+
/** Pending answers for a session (PollResponse extension). */
|
|
2094
|
+
pendingFor(sessionId) {
|
|
2095
|
+
return this.relay.pendingFor(sessionId);
|
|
1870
2096
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
return
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
}
|
|
1967
|
-
/**
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
}
|
|
1979
|
-
/**
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
description: "Built for creating custom agent presets, with all Standard mode capabilities plus runtime inspection, plugin experiments, and preset-authoring guidance."
|
|
2097
|
+
/** Recent settlements for a session (ring: delivered-then-pruned). */
|
|
2098
|
+
settlementsFor(sessionId) {
|
|
2099
|
+
return this.relay.settlementsFor(sessionId);
|
|
2100
|
+
}
|
|
2101
|
+
/** Delivery hook: drop settlements the client has already seen. */
|
|
2102
|
+
pruneSettlements(sessionId, seenRpcIds) {
|
|
2103
|
+
this.relay.pruneSettlements(sessionId, seenRpcIds);
|
|
2104
|
+
}
|
|
2105
|
+
/** Normalized session/list with fork-title healing. */
|
|
2106
|
+
async listSessions() {
|
|
2107
|
+
return this.relay.listSessions();
|
|
2108
|
+
}
|
|
2109
|
+
/** The workspace-RPC OWNER for a session (parent-chain walk). */
|
|
2110
|
+
async workspaceOwnerSessionId(sessionId) {
|
|
2111
|
+
return this.relay.workspaceOwnerSessionId(sessionId);
|
|
2112
|
+
}
|
|
2113
|
+
/** The session/page + session/follow address for one session id. */
|
|
2114
|
+
addressFor(sessionId) {
|
|
2115
|
+
return this.relay.addressFor(sessionId);
|
|
2116
|
+
}
|
|
2117
|
+
/** session.history successor (0.1.2 session/page tail). */
|
|
2118
|
+
async history(sessionId) {
|
|
2119
|
+
return this.relay.history(sessionId);
|
|
2120
|
+
}
|
|
2121
|
+
/** Workspace registry adoption (Add-workspace button's host call). */
|
|
2122
|
+
async createWorkspace(path) {
|
|
2123
|
+
return this.relay.createWorkspace(path);
|
|
2124
|
+
}
|
|
2125
|
+
/** Workspace rename (Chip Menu ADR D3). */
|
|
2126
|
+
async renameWorkspace(workspaceId, title) {
|
|
2127
|
+
return this.relay.renameWorkspace(workspaceId, title);
|
|
2128
|
+
}
|
|
2129
|
+
/** Workspace delete (Chip Menu ADR D3). */
|
|
2130
|
+
async deleteWorkspace(workspaceId) {
|
|
2131
|
+
return this.relay.deleteWorkspace(workspaceId);
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* Goal Bar (2026-09-08, ADR amendment): one goal mutation through the
|
|
2135
|
+
* host's Goal Remotes — goals/pause | goals/resume | goals/clear |
|
|
2136
|
+
* goals/edit with the CAS ref {id, revision} (a stale revision loses
|
|
2137
|
+
* host-side). goals/edit additionally carries the changed-fields
|
|
2138
|
+
* request {objective?, maxGoalRounds?} (Goal Editor, 2026-09-09). The
|
|
2139
|
+
* raw RemoteResult passes through; the route maps refusals.
|
|
2140
|
+
* Kept self-contained in the shell: tests pin it via
|
|
2141
|
+
* DshConnection.prototype.goalVerb.call(fakeRpcThis, …).
|
|
2142
|
+
*/
|
|
2143
|
+
async goalVerb(verb, sessionId, ref, request) {
|
|
2144
|
+
return this.rpc(`goals/${verb}`, request === void 0 ? {
|
|
2145
|
+
agentId: sessionId,
|
|
2146
|
+
ref
|
|
2147
|
+
} : {
|
|
2148
|
+
agentId: sessionId,
|
|
2149
|
+
ref,
|
|
2150
|
+
request
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
/** workspaceFiles/read — one page of a UTF-8 text file. */
|
|
2154
|
+
async readWorkspaceFile(sessionId, path, range) {
|
|
2155
|
+
return this.relay.readWorkspaceFile(sessionId, path, range);
|
|
2156
|
+
}
|
|
2157
|
+
/** workspaceFiles/readBytes — one byte window, raw bytes. */
|
|
2158
|
+
async readWorkspaceFileBytes(sessionId, path, range) {
|
|
2159
|
+
return this.relay.readWorkspaceFileBytes(sessionId, path, range);
|
|
2160
|
+
}
|
|
2161
|
+
/** workspaceFiles/list — one directory level. */
|
|
2162
|
+
async listWorkspaceDirectory(sessionId, path) {
|
|
2163
|
+
return this.relay.listWorkspaceDirectory(sessionId, path);
|
|
2164
|
+
}
|
|
2165
|
+
/** host.listDirectory — the folder picker browse. */
|
|
2166
|
+
async listDirectory(path) {
|
|
2167
|
+
return this.relay.listDirectory(path);
|
|
2168
|
+
}
|
|
2169
|
+
/** session.attachment — session-authorized durable image read. */
|
|
2170
|
+
async readAttachment(sessionId, attachmentId) {
|
|
2171
|
+
return this.relay.readAttachment(sessionId, attachmentId);
|
|
2172
|
+
}
|
|
2173
|
+
/** host.pickDirectory — the NATIVE directory chooser. */
|
|
2174
|
+
async pickDirectory(signal) {
|
|
2175
|
+
return this.relay.pickDirectory(signal);
|
|
2176
|
+
}
|
|
2177
|
+
/** session.history PAGE ending at beforeSeq−1 (ledger truth, BC-4). */
|
|
2178
|
+
async historyPage(sessionId, beforeSeq) {
|
|
2179
|
+
return this.relay.historyPage(sessionId, beforeSeq);
|
|
2180
|
+
}
|
|
2181
|
+
/** session.create {cwd?, agentPreset?}. */
|
|
2182
|
+
async createSession(cwd, agentPreset) {
|
|
2183
|
+
return this.relay.createSession(cwd, agentPreset);
|
|
2184
|
+
}
|
|
2185
|
+
/** session/fork — the fork child. */
|
|
2186
|
+
async forkSession(sessionId, atSeq) {
|
|
2187
|
+
return this.relay.forkSession(sessionId, atSeq);
|
|
2188
|
+
}
|
|
2189
|
+
/** agentPreset.list — the picker's only source of truth. */
|
|
2190
|
+
async listPresets() {
|
|
2191
|
+
return this.relay.listPresets();
|
|
2192
|
+
}
|
|
2193
|
+
/** session.prompt — returns after the receipt only (BC-3). */
|
|
2194
|
+
async prompt(sessionId, payload, mode = "queue") {
|
|
2195
|
+
return this.relay.prompt(sessionId, payload, mode);
|
|
2196
|
+
}
|
|
2197
|
+
/** commands/execute — the NATIVE slash-command surface. */
|
|
2198
|
+
async executeCommand(sessionId, line) {
|
|
2199
|
+
return this.relay.executeCommand(sessionId, line);
|
|
2200
|
+
}
|
|
2201
|
+
/** commands/list — the host's command catalog. */
|
|
2202
|
+
async listCommands(sessionId) {
|
|
2203
|
+
return this.relay.listCommands(sessionId);
|
|
2204
|
+
}
|
|
2205
|
+
/** skills/list — the session's user-invocable skill catalog. */
|
|
2206
|
+
async listSkills(sessionId) {
|
|
2207
|
+
return this.relay.listSkills(sessionId);
|
|
2208
|
+
}
|
|
2209
|
+
/** session.cancel */
|
|
2210
|
+
async cancel(sessionId) {
|
|
2211
|
+
return this.relay.cancel(sessionId);
|
|
2212
|
+
}
|
|
2213
|
+
/** session.rename {sessionId, title}. */
|
|
2214
|
+
async renameSession(sessionId, title) {
|
|
2215
|
+
return this.relay.renameSession(sessionId, title);
|
|
2216
|
+
}
|
|
2217
|
+
/** session.models → the model directory. */
|
|
2218
|
+
async listModels(_sessionId) {
|
|
2219
|
+
return this.relay.listModels(_sessionId);
|
|
2220
|
+
}
|
|
2221
|
+
/** session.selectModel → {selected: ModelSelection}. */
|
|
2222
|
+
async selectModel(sessionId, provider, model, reasoningEffort) {
|
|
2223
|
+
return this.relay.selectModel(sessionId, provider, model, reasoningEffort);
|
|
2224
|
+
}
|
|
2225
|
+
/** Answer a pending approval/question via $events/result. */
|
|
2226
|
+
async respond(rpcId, payload) {
|
|
2227
|
+
return this.relay.respond(rpcId, payload);
|
|
2228
|
+
}
|
|
2229
|
+
/** Authenticated GET of one Host path (cookie ladder). */
|
|
2230
|
+
async fetchHostPath(path, signal) {
|
|
2231
|
+
return this.relay.fetchHostPath(path, signal);
|
|
2232
|
+
}
|
|
2233
|
+
/** Resync: replace the ring buffer with the ledger's tail page (BC-4). */
|
|
2234
|
+
async resyncFromLedger(sessionId) {
|
|
2235
|
+
return this.relay.resyncFromLedger(sessionId);
|
|
2236
|
+
}
|
|
2237
|
+
dispose() {
|
|
2238
|
+
this.disposed = true;
|
|
2239
|
+
this.downlinks.teardownSockets();
|
|
2240
|
+
this.relay.dispose();
|
|
2241
|
+
this.sessions.dispose();
|
|
2017
2242
|
}
|
|
2018
2243
|
};
|
|
2019
|
-
/**
|
|
2020
|
-
* Conservative normalize: unknown shapes keep their id but never invent data.
|
|
2021
|
-
* The one English overlay seat (SHIPPED_PRESET_ENGLISH) applies before the
|
|
2022
|
-
* host fallbacks — a shipped preset's label is locale-resolved here, never
|
|
2023
|
-
* the wire's unlocalized text. Operator-tunable via
|
|
2024
|
-
* ~/.dsi/settings.yaml `dsh.presetEnglish` (read live per listPresets
|
|
2025
|
-
* call, passed through here): false hands the wire name through untouched.
|
|
2026
|
-
*/
|
|
2027
|
-
function normalizePreset(row, overlayEnglish = true) {
|
|
2028
|
-
const id = typeof row.id === "string" ? row.id : String(row.id ?? "");
|
|
2029
|
-
const shipped = overlayEnglish && row.trust === "system" ? SHIPPED_PRESET_ENGLISH[id] : void 0;
|
|
2030
|
-
return {
|
|
2031
|
-
id,
|
|
2032
|
-
name: shipped?.name ?? (typeof row.name === "string" ? row.name : null),
|
|
2033
|
-
description: shipped?.description ?? (typeof row.description === "string" ? row.description : null),
|
|
2034
|
-
isDefault: row.isDefault === true
|
|
2035
|
-
};
|
|
2036
|
-
}
|
|
2037
2244
|
|
|
2038
2245
|
export { DshRpcError as D, isPromptImageMediaType as a, buildPromptContent as b, getDshConnection as g, isSubagentRejection as i, mapRpcFailure as m, statusFor as s };
|
|
2039
|
-
//# sourceMappingURL=dsh-connection.js-
|
|
2246
|
+
//# sourceMappingURL=dsh-connection.js-DRtqPR_j.js.map
|