dsh-context 0.66.1 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * dsh-context v0.66.1
2
+ * dsh-context v0.68.0
3
3
  * A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.
4
4
  * @author bowenliang123
5
5
  * @license Apache-2.0
6
6
  * @homepage https://github.com/bowenliang123/dsh-context
7
- * @built 2026-10-09T09:32:06+00:00
8
- * @commit def8b2b74a8e
9
- * @commit-date 2026-10-09T17:29:50+08:00
7
+ * @built 2026-10-11T02:40:54+00:00
8
+ * @commit 425fd2e7b82e
9
+ * @commit-date 2026-10-11T10:37:14+08:00
10
10
  */
11
11
 
12
12
  import { createRequire } from "node:module";
@@ -18,21 +18,6 @@ import { dirname, isAbsolute, join, normalize, relative } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import { getHeapStatistics } from "node:v8";
20
20
  import { randomUUID } from "node:crypto";
21
- //#region src/shared/providers.ts
22
- const MODELS_DEV_PROVIDER_IDS = {
23
- "deepseek-official": "deepseek",
24
- "deepseek-account": "deepseek",
25
- "kimi-coding": "moonshotai",
26
- "minimax-cn": "minimax",
27
- "zai-coding-cn": "zhipuai"
28
- };
29
- function modelsDevProviderOf(dshProviderId) {
30
- return MODELS_DEV_PROVIDER_IDS[dshProviderId] ?? dshProviderId;
31
- }
32
- function isDeepSeekProvider(dshProviderId) {
33
- return modelsDevProviderOf(dshProviderId) === "deepseek";
34
- }
35
- //#endregion
36
21
  //#region src/shared/estimate.ts
37
22
  const CHARS_PER_TOKEN$1 = 4;
38
23
  const ROLE_OVERHEAD$1 = 4;
@@ -548,7 +533,7 @@ function opsOfCall(input) {
548
533
  }];
549
534
  }
550
535
  //#endregion
551
- //#region src/host/fold.ts
536
+ //#region src/host/foldState.ts
552
537
  function trimToLastTurns(requests, maxTurns) {
553
538
  let runs = 0;
554
539
  let start = requests.length;
@@ -624,6 +609,280 @@ function createTimelineState() {
624
609
  spans: []
625
610
  };
626
611
  }
612
+ //#endregion
613
+ //#region src/shared/providers.ts
614
+ const MODELS_DEV_PROVIDER_IDS = {
615
+ "deepseek-official": "deepseek",
616
+ "deepseek-account": "deepseek",
617
+ "kimi-coding": "moonshotai",
618
+ "minimax-cn": "minimax",
619
+ "zai-coding-cn": "zhipuai"
620
+ };
621
+ function modelsDevProviderOf(dshProviderId) {
622
+ return MODELS_DEV_PROVIDER_IDS[dshProviderId] ?? dshProviderId;
623
+ }
624
+ function isDeepSeekProvider(dshProviderId) {
625
+ return modelsDevProviderOf(dshProviderId) === "deepseek";
626
+ }
627
+ //#endregion
628
+ //#region src/host/foldMetering.ts
629
+ function tokenCountOf(value) {
630
+ if (typeof value === "number") return Number.isFinite(value) ? Math.max(0, Math.round(value)) : null;
631
+ if (typeof value === "string" && value.trim() !== "") {
632
+ const parsed = Number(value);
633
+ return Number.isFinite(parsed) ? Math.max(0, Math.round(parsed)) : null;
634
+ }
635
+ return null;
636
+ }
637
+ function isPeakUtc(time) {
638
+ const at = new Date(time);
639
+ const day = at.getUTCDay();
640
+ if (day === 0 || day === 6) return false;
641
+ const h = at.getUTCHours();
642
+ return h >= 1 && h < 4 || h >= 6 && h < 10;
643
+ }
644
+ function addBilledUsage(prev, provider, model, time, usage) {
645
+ const period = isDeepSeekProvider(provider) && !isPeakUtc(time) ? "off" : "peak";
646
+ const models = prev?.[provider] ?? {};
647
+ const periods = models[model] ?? {};
648
+ const b = periods[period] ?? {
649
+ uncached: 0,
650
+ cacheRead: 0,
651
+ cacheWrite: 0,
652
+ output: 0
653
+ };
654
+ const nextPeriods = { ...periods };
655
+ nextPeriods[period] = {
656
+ uncached: b.uncached + usage.uncached,
657
+ cacheRead: b.cacheRead + usage.cacheRead,
658
+ cacheWrite: b.cacheWrite + usage.cacheWrite,
659
+ output: b.output + usage.output
660
+ };
661
+ const nextModels = {
662
+ ...models,
663
+ [model]: nextPeriods
664
+ };
665
+ return {
666
+ ...prev ?? {},
667
+ [provider]: nextModels
668
+ };
669
+ }
670
+ function copyCostUsage(cost) {
671
+ const copy = {};
672
+ for (const provider in cost) {
673
+ const models = {};
674
+ for (const model in cost[provider]) {
675
+ const periods = cost[provider][model];
676
+ const modelCopy = {};
677
+ if (periods.peak !== void 0) modelCopy.peak = { ...periods.peak };
678
+ if (periods.off !== void 0) modelCopy.off = { ...periods.off };
679
+ models[model] = modelCopy;
680
+ }
681
+ copy[provider] = models;
682
+ }
683
+ return copy;
684
+ }
685
+ function accumulateCost(st, time, usage) {
686
+ const model = st.model;
687
+ if (model === void 0) return;
688
+ st.cost = addBilledUsage(st.cost, st.provider ?? "", model, time, usage);
689
+ }
690
+ const TOOL_TIMING_CAP = 16;
691
+ const DECODE_KINDS = [
692
+ "reasoning",
693
+ "text",
694
+ "toolarg"
695
+ ];
696
+ function durOf(from, to) {
697
+ if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
698
+ return Math.max(0, to - from);
699
+ }
700
+ function ensureTiming(st) {
701
+ if (st.timing === void 0) st.timing = {
702
+ wallMs: 0,
703
+ ttftMs: 0,
704
+ genMs: 0,
705
+ calls: 0,
706
+ toolsMs: 0,
707
+ toolCalls: 0,
708
+ tools: {}
709
+ };
710
+ return st.timing;
711
+ }
712
+ const DECODE_FIELDS = {
713
+ reasoning: {
714
+ ms: "reasoningMs",
715
+ n: "reasoningBlocks"
716
+ },
717
+ text: {
718
+ ms: "textMs",
719
+ n: "textBlocks"
720
+ },
721
+ toolarg: {
722
+ ms: "toolArgMs",
723
+ n: "toolArgBlocks"
724
+ }
725
+ };
726
+ function addDecode(timing, kind, ms, blocks) {
727
+ const field = DECODE_FIELDS[kind];
728
+ if (ms > 0) timing[field.ms] = (timing[field.ms] ?? 0) + ms;
729
+ if (blocks > 0) timing[field.n] = (timing[field.n] ?? 0) + blocks;
730
+ }
731
+ function bumpToolTotals(timing, name, ms) {
732
+ if (name === "__proto__") return;
733
+ if (!Object.hasOwn(timing.tools, name)) {
734
+ if (Object.keys(timing.tools).length >= TOOL_TIMING_CAP) {
735
+ let minKey = "";
736
+ let minMs = Infinity;
737
+ for (const k in timing.tools) if (timing.tools[k].ms < minMs) {
738
+ minMs = timing.tools[k].ms;
739
+ minKey = k;
740
+ }
741
+ const kept = {};
742
+ for (const k in timing.tools) if (k !== minKey) kept[k] = timing.tools[k];
743
+ timing.tools = kept;
744
+ }
745
+ timing.tools[name] = {
746
+ calls: 1,
747
+ ms
748
+ };
749
+ return;
750
+ }
751
+ const cur = timing.tools[name];
752
+ timing.tools[name] = {
753
+ calls: cur.calls + 1,
754
+ ms: cur.ms + ms
755
+ };
756
+ }
757
+ //#endregion
758
+ //#region src/host/foldWire.ts
759
+ function headFieldsOf(state) {
760
+ const surfaceTotal = state.sums.user + state.sums.inject + state.sums.skill + state.sums.assistant + state.sums.tool;
761
+ const result = {
762
+ ok: true,
763
+ ...state.model !== void 0 ? { model: state.model } : {},
764
+ ...state.provider !== void 0 ? { provider: state.provider } : {},
765
+ ...state.contextWindow !== void 0 ? { contextWindow: state.contextWindow } : {},
766
+ current: {
767
+ system: state.systemTokens,
768
+ tools: state.toolsTokens,
769
+ user: state.sums.user,
770
+ inject: state.sums.inject,
771
+ skill: state.sums.skill,
772
+ assistant: state.sums.assistant,
773
+ tool: state.sums.tool,
774
+ total: surfaceTotal + state.systemTokens + state.toolsTokens
775
+ },
776
+ images: state.surface.reduce((n, node) => n + (node.imgs ?? 0), 0),
777
+ toolCalls: state.surface.reduce((n, node) => node.cat === "tool" || node.cat === "skill" && node.tool !== void 0 ? n + 1 : n, 0),
778
+ humanInputs: state.humanInputs ?? 0,
779
+ answers: state.answers ?? 0,
780
+ ...state.lastUser !== void 0 ? { lastUser: state.lastUser } : {},
781
+ requests: [],
782
+ events: [],
783
+ nodes: [],
784
+ droppedNodes: 0,
785
+ archive: []
786
+ };
787
+ if (state.cost !== void 0) result.cost = copyCostUsage(state.cost);
788
+ if (state.timing !== void 0) {
789
+ const tools = {};
790
+ for (const k in state.timing.tools) tools[k] = { ...state.timing.tools[k] };
791
+ result.timing = {
792
+ ...state.timing,
793
+ tools
794
+ };
795
+ }
796
+ if (state.systems !== void 0 && state.systems.length > 0) result.systems = state.systems.map((n) => ({ ...n }));
797
+ return result;
798
+ }
799
+ function detailCollectionsOf(state, bounds) {
800
+ const result = {
801
+ requests: state.requests.map((r) => ({ ...r })),
802
+ events: state.events.map((e) => ({ ...e })),
803
+ nodes: [],
804
+ droppedNodes: 0,
805
+ archive: state.archived.map((n) => ({ ...n })),
806
+ fileOps: state.fileOps.map((o) => ({ ...o })),
807
+ ...state.fileOpsFloor !== void 0 ? { fileOpsFloor: state.fileOpsFloor } : {},
808
+ spans: state.spans.map((s) => ({ ...s }))
809
+ };
810
+ const overflowCount = Math.max(0, state.surface.length - bounds.maxNodes);
811
+ const overflow = state.surface.slice(0, overflowCount);
812
+ const tail = state.surface.slice(overflowCount);
813
+ const pinned = overflow.filter((n) => n.cat === "inject" || n.cat === "skill");
814
+ result.nodes = pinned.length > 0 ? [...pinned, ...tail] : tail;
815
+ result.droppedNodes = overflowCount - pinned.length;
816
+ if (result.droppedNodes > 0) {
817
+ let floor = 0;
818
+ for (const n of overflow) if (n.cat !== "inject" && n.cat !== "skill") floor = Math.max(floor, n.seq);
819
+ result.surfaceFloor = floor;
820
+ }
821
+ if (state.archiveFloor !== void 0) result.archiveFloor = state.archiveFloor;
822
+ const requests = result.requests;
823
+ const events = result.events;
824
+ let ri = 0;
825
+ for (const ev of events) {
826
+ while (ri < requests.length && requests[ri].seq <= ev.seq) ri++;
827
+ const next = requests.at(ri);
828
+ const prev = ri > 0 ? requests.at(ri - 1) : void 0;
829
+ if (next !== void 0 && typeof next.turn === "number" && typeof next.step === "number") {
830
+ ev.turn = next.turn;
831
+ ev.step = next.step;
832
+ }
833
+ if (prev !== void 0 && typeof prev.turn === "number" && typeof prev.step === "number") {
834
+ ev.fromTurn = prev.turn;
835
+ ev.fromStep = prev.step;
836
+ }
837
+ }
838
+ return result;
839
+ }
840
+ function buildTimelineHead(state) {
841
+ const result = headFieldsOf(state);
842
+ const turns = new Set();
843
+ for (const r of state.requests) turns.add(r.turn ?? 0);
844
+ let injects = 0;
845
+ let compactions = 0;
846
+ let prunes = 0;
847
+ const skills = new Set();
848
+ for (const e of state.events) {
849
+ if (e.kind === "inject") injects++;
850
+ else if (e.kind === "compaction") compactions++;
851
+ else if (e.kind === "prune") prunes++;
852
+ if (e.sub === "skill" && typeof e.name === "string" && e.name !== "") skills.add(e.name);
853
+ }
854
+ result.counts = {
855
+ turns: turns.size,
856
+ steps: state.requests.length,
857
+ injects,
858
+ compactions,
859
+ prunes,
860
+ skills: skills.size
861
+ };
862
+ const last = state.requests.at(-1);
863
+ if (last !== void 0) result.last = {
864
+ seq: last.seq,
865
+ total: last.total,
866
+ ...typeof last.prompt === "number" ? { prompt: last.prompt } : {}
867
+ };
868
+ result.detailRev = state.detailRev ?? 0;
869
+ return result;
870
+ }
871
+ function buildTimelineDetail(state, bounds) {
872
+ return {
873
+ rev: state.detailRev ?? 0,
874
+ head: buildTimelineHead(state),
875
+ ...detailCollectionsOf(state, bounds)
876
+ };
877
+ }
878
+ function buildTimelineView(state, bounds) {
879
+ return {
880
+ ...headFieldsOf(state),
881
+ ...detailCollectionsOf(state, bounds)
882
+ };
883
+ }
884
+ //#endregion
885
+ //#region src/host/fold.ts
627
886
  function categoryOf(type, message) {
628
887
  if (type === "assistant/message") return "assistant";
629
888
  if (type === "tool/result") return "tool";
@@ -687,7 +946,7 @@ function removeSurfaceSeqs(st, claimed, goneSeq) {
687
946
  st.surface = kept;
688
947
  return removed;
689
948
  }
690
- function messageOf$1(data) {
949
+ function messageOf$2(data) {
691
950
  const message = data?.message;
692
951
  return message !== null && typeof message === "object" ? message : null;
693
952
  }
@@ -793,125 +1052,13 @@ function applySurface(st, ev, type, message) {
793
1052
  for (const r of removed) st.sums[r.cat] -= r.tokens;
794
1053
  st.sums[cat] += node.tokens;
795
1054
  return node;
796
- }
797
- }
798
- st.surface.push(node);
799
- st.sums[cat] += node.tokens;
800
- return node;
801
- }
802
- function tokenCountOf(value) {
803
- if (typeof value === "number") return Number.isFinite(value) ? Math.max(0, Math.round(value)) : null;
804
- if (typeof value === "string" && value.trim() !== "") {
805
- const parsed = Number(value);
806
- return Number.isFinite(parsed) ? Math.max(0, Math.round(parsed)) : null;
807
- }
808
- return null;
809
- }
810
- function isPeakUtc(time) {
811
- const at = new Date(time);
812
- const day = at.getUTCDay();
813
- if (day === 0 || day === 6) return false;
814
- const h = at.getUTCHours();
815
- return h >= 1 && h < 4 || h >= 6 && h < 10;
816
- }
817
- function addBilledUsage(prev, provider, model, time, usage) {
818
- const period = isDeepSeekProvider(provider) && !isPeakUtc(time) ? "off" : "peak";
819
- const models = prev?.[provider] ?? {};
820
- const periods = models[model] ?? {};
821
- const b = periods[period] ?? {
822
- uncached: 0,
823
- cacheRead: 0,
824
- cacheWrite: 0,
825
- output: 0
826
- };
827
- const nextPeriods = { ...periods };
828
- nextPeriods[period] = {
829
- uncached: b.uncached + usage.input,
830
- cacheRead: b.cacheRead + usage.cacheRead,
831
- cacheWrite: b.cacheWrite + usage.cacheWrite,
832
- output: b.output + usage.output
833
- };
834
- const nextModels = {
835
- ...models,
836
- [model]: nextPeriods
837
- };
838
- return {
839
- ...prev ?? {},
840
- [provider]: nextModels
841
- };
842
- }
843
- function accumulateCost(st, time, usage) {
844
- const model = st.model;
845
- if (model === void 0) return;
846
- st.cost = addBilledUsage(st.cost, st.provider ?? "", model, time, usage);
847
- }
848
- const TOOL_TIMING_CAP = 16;
849
- const ASK_USER_TOOL = "ask_user_question";
850
- const DECODE_KINDS = [
851
- "reasoning",
852
- "text",
853
- "toolarg"
854
- ];
855
- function durOf(from, to) {
856
- if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
857
- return Math.max(0, to - from);
858
- }
859
- function ensureTiming(st) {
860
- if (st.timing === void 0) st.timing = {
861
- wallMs: 0,
862
- ttftMs: 0,
863
- genMs: 0,
864
- calls: 0,
865
- toolsMs: 0,
866
- toolCalls: 0,
867
- tools: {}
868
- };
869
- return st.timing;
870
- }
871
- const DECODE_FIELDS = {
872
- reasoning: {
873
- ms: "reasoningMs",
874
- n: "reasoningBlocks"
875
- },
876
- text: {
877
- ms: "textMs",
878
- n: "textBlocks"
879
- },
880
- toolarg: {
881
- ms: "toolArgMs",
882
- n: "toolArgBlocks"
883
- }
884
- };
885
- function addDecode(timing, kind, ms, blocks) {
886
- const field = DECODE_FIELDS[kind];
887
- if (ms > 0) timing[field.ms] = (timing[field.ms] ?? 0) + ms;
888
- if (blocks > 0) timing[field.n] = (timing[field.n] ?? 0) + blocks;
889
- }
890
- function bumpToolTotals(timing, name, ms) {
891
- if (!Object.hasOwn(timing.tools, name)) {
892
- if (Object.keys(timing.tools).length >= TOOL_TIMING_CAP) {
893
- let minKey = "";
894
- let minMs = Infinity;
895
- for (const k in timing.tools) if (timing.tools[k].ms < minMs) {
896
- minMs = timing.tools[k].ms;
897
- minKey = k;
898
- }
899
- const kept = {};
900
- for (const k in timing.tools) if (k !== minKey) kept[k] = timing.tools[k];
901
- timing.tools = kept;
902
- }
903
- timing.tools[name] = {
904
- calls: 1,
905
- ms
906
- };
907
- return;
908
- }
909
- const cur = timing.tools[name];
910
- timing.tools[name] = {
911
- calls: cur.calls + 1,
912
- ms: cur.ms + ms
913
- };
1055
+ }
1056
+ }
1057
+ st.surface.push(node);
1058
+ st.sums[cat] += node.tokens;
1059
+ return node;
914
1060
  }
1061
+ const ASK_USER_TOOL = "ask_user_question";
915
1062
  const CLONE_ALL = [
916
1063
  "surface",
917
1064
  "sums",
@@ -980,7 +1127,7 @@ function applyTimeline(state, event, bounds) {
980
1127
  pushSystem(s, {
981
1128
  seq: event.seq,
982
1129
  time: event.time,
983
- tokens: estimateSystemContent(messageOf$1(data)?.content)
1130
+ tokens: estimateSystemContent(messageOf$2(data)?.content)
984
1131
  });
985
1132
  break;
986
1133
  }
@@ -1137,7 +1284,7 @@ function applyTimeline(state, event, bounds) {
1137
1284
  case "user/message":
1138
1285
  case "developer/message": {
1139
1286
  const developer = event.type === "developer/message";
1140
- const msg = developer ? messageOf$1(data) : deriveEventMessage(event);
1287
+ const msg = developer ? messageOf$2(data) : deriveEventMessage(event);
1141
1288
  if (developer && (msg === null || !Array.isArray(msg.content))) return state;
1142
1289
  const s = ensure([
1143
1290
  "surface",
@@ -1273,7 +1420,7 @@ function applyTimeline(state, event, bounds) {
1273
1420
  if (cacheRead !== null) record.cacheRead = cacheRead;
1274
1421
  if (output !== null) record.output = output;
1275
1422
  accumulateCost(s, event.time, {
1276
- input: input ?? 0,
1423
+ uncached: input ?? 0,
1277
1424
  cacheRead: cacheRead ?? 0,
1278
1425
  cacheWrite: cacheWrite ?? 0,
1279
1426
  output: output ?? 0
@@ -1367,145 +1514,6 @@ function applyTimeline(state, event, bounds) {
1367
1514
  }
1368
1515
  return state;
1369
1516
  }
1370
- function headFieldsOf(state) {
1371
- const surfaceTotal = state.sums.user + state.sums.inject + state.sums.skill + state.sums.assistant + state.sums.tool;
1372
- const result = {
1373
- ok: true,
1374
- ...state.model !== void 0 ? { model: state.model } : {},
1375
- ...state.provider !== void 0 ? { provider: state.provider } : {},
1376
- ...state.contextWindow !== void 0 ? { contextWindow: state.contextWindow } : {},
1377
- current: {
1378
- system: state.systemTokens,
1379
- tools: state.toolsTokens,
1380
- user: state.sums.user,
1381
- inject: state.sums.inject,
1382
- skill: state.sums.skill,
1383
- assistant: state.sums.assistant,
1384
- tool: state.sums.tool,
1385
- total: surfaceTotal + state.systemTokens + state.toolsTokens
1386
- },
1387
- images: state.surface.reduce((n, node) => n + (node.imgs ?? 0), 0),
1388
- toolCalls: state.surface.reduce((n, node) => node.cat === "tool" || node.cat === "skill" && node.tool !== void 0 ? n + 1 : n, 0),
1389
- humanInputs: state.humanInputs ?? 0,
1390
- answers: state.answers ?? 0,
1391
- ...state.lastUser !== void 0 ? { lastUser: state.lastUser } : {},
1392
- requests: [],
1393
- events: [],
1394
- nodes: [],
1395
- droppedNodes: 0,
1396
- archive: []
1397
- };
1398
- if (state.cost !== void 0) {
1399
- const cost = {};
1400
- for (const provider in state.cost) {
1401
- const models = {};
1402
- for (const model in state.cost[provider]) {
1403
- const periods = state.cost[provider][model];
1404
- const copy = {};
1405
- if (periods.peak !== void 0) copy.peak = { ...periods.peak };
1406
- if (periods.off !== void 0) copy.off = { ...periods.off };
1407
- models[model] = copy;
1408
- }
1409
- cost[provider] = models;
1410
- }
1411
- result.cost = cost;
1412
- }
1413
- if (state.timing !== void 0) {
1414
- const tools = {};
1415
- for (const k in state.timing.tools) tools[k] = { ...state.timing.tools[k] };
1416
- result.timing = {
1417
- ...state.timing,
1418
- tools
1419
- };
1420
- }
1421
- if (state.systems !== void 0 && state.systems.length > 0) result.systems = state.systems.map((n) => ({ ...n }));
1422
- return result;
1423
- }
1424
- function detailCollectionsOf(state, bounds) {
1425
- const result = {
1426
- requests: state.requests.map((r) => ({ ...r })),
1427
- events: state.events.map((e) => ({ ...e })),
1428
- nodes: [],
1429
- droppedNodes: 0,
1430
- archive: state.archived.map((n) => ({ ...n })),
1431
- fileOps: state.fileOps.map((o) => ({ ...o })),
1432
- ...state.fileOpsFloor !== void 0 ? { fileOpsFloor: state.fileOpsFloor } : {},
1433
- spans: state.spans.map((s) => ({ ...s }))
1434
- };
1435
- const overflowCount = Math.max(0, state.surface.length - bounds.maxNodes);
1436
- const overflow = state.surface.slice(0, overflowCount);
1437
- const tail = state.surface.slice(overflowCount);
1438
- const pinned = overflow.filter((n) => n.cat === "inject" || n.cat === "skill");
1439
- result.nodes = pinned.length > 0 ? [...pinned, ...tail] : tail;
1440
- result.droppedNodes = overflowCount - pinned.length;
1441
- if (result.droppedNodes > 0) {
1442
- let floor = 0;
1443
- for (const n of overflow) if (n.cat !== "inject" && n.cat !== "skill") floor = Math.max(floor, n.seq);
1444
- result.surfaceFloor = floor;
1445
- }
1446
- if (state.archiveFloor !== void 0) result.archiveFloor = state.archiveFloor;
1447
- const requests = result.requests;
1448
- const events = result.events;
1449
- let ri = 0;
1450
- for (const ev of events) {
1451
- while (ri < requests.length && requests[ri].seq <= ev.seq) ri++;
1452
- const next = requests.at(ri);
1453
- const prev = ri > 0 ? requests.at(ri - 1) : void 0;
1454
- if (next !== void 0 && typeof next.turn === "number" && typeof next.step === "number") {
1455
- ev.turn = next.turn;
1456
- ev.step = next.step;
1457
- }
1458
- if (prev !== void 0 && typeof prev.turn === "number" && typeof prev.step === "number") {
1459
- ev.fromTurn = prev.turn;
1460
- ev.fromStep = prev.step;
1461
- }
1462
- }
1463
- return result;
1464
- }
1465
- function buildTimelineHead(state) {
1466
- const result = headFieldsOf(state);
1467
- const turns = new Set();
1468
- for (const r of state.requests) turns.add(r.turn ?? 0);
1469
- let injects = 0;
1470
- let compactions = 0;
1471
- let prunes = 0;
1472
- const skills = new Set();
1473
- for (const e of state.events) {
1474
- if (e.kind === "inject") injects++;
1475
- else if (e.kind === "compaction") compactions++;
1476
- else if (e.kind === "prune") prunes++;
1477
- if (e.sub === "skill" && typeof e.name === "string" && e.name !== "") skills.add(e.name);
1478
- }
1479
- result.counts = {
1480
- turns: turns.size,
1481
- steps: state.requests.length,
1482
- injects,
1483
- compactions,
1484
- prunes,
1485
- skills: skills.size
1486
- };
1487
- const last = state.requests.at(-1);
1488
- if (last !== void 0) result.last = {
1489
- seq: last.seq,
1490
- total: last.total,
1491
- ...typeof last.prompt === "number" ? { prompt: last.prompt } : {}
1492
- };
1493
- result.detailRev = state.detailRev ?? 0;
1494
- return result;
1495
- }
1496
- function buildTimelineDetail(state, bounds) {
1497
- return {
1498
- rev: state.detailRev ?? 0,
1499
- head: buildTimelineHead(state),
1500
- ...detailCollectionsOf(state, bounds)
1501
- };
1502
- }
1503
- function buildTimelineView(state, bounds) {
1504
- return {
1505
- ...headFieldsOf(state),
1506
- ...detailCollectionsOf(state, bounds)
1507
- };
1508
- }
1509
1517
  //#endregion
1510
1518
  //#region src/host/config.ts
1511
1519
  const DEFAULT_BOUNDS = {
@@ -1549,7 +1557,8 @@ const Config = z$1.object({
1549
1557
  "path"
1550
1558
  ]).default("count").loose()),
1551
1559
  insightsEntry: volatileField(z$1.union(["show", "hide"]).default("show").loose()),
1552
- defaultDurationCurve: volatileField(z$1.union(["show", "hide"]).default("show").loose())
1560
+ defaultDurationCurve: volatileField(z$1.union(["show", "hide"]).default("show").loose()),
1561
+ fleetTab: volatileField(z$1.union(["show", "hide"]).default("show").loose())
1553
1562
  });
1554
1563
  function resolveBounds(config) {
1555
1564
  const resolved = Config(config ?? {});
@@ -1865,7 +1874,7 @@ function billedBucketsOf(value) {
1865
1874
  const output = tokenCountOf(usage.outputTokens);
1866
1875
  if (input === null && cacheRead === null && cacheWrite === null && output === null) return null;
1867
1876
  return {
1868
- input: input ?? 0,
1877
+ uncached: input ?? 0,
1869
1878
  cacheRead: cacheRead ?? 0,
1870
1879
  cacheWrite: cacheWrite ?? 0,
1871
1880
  output: output ?? 0
@@ -1993,7 +2002,7 @@ function applyActivity(state, event) {
1993
2002
  let cost = prev?.cost;
1994
2003
  if (buckets !== null && state.model !== void 0) cost = addBilledUsage(cost, state.provider ?? "", state.model, initiated, buckets);
1995
2004
  return withDayEntry(state, key, {
1996
- tokens: (prev === void 0 ? 0 : prev.tokens) + (buckets === null ? 0 : buckets.input + buckets.cacheRead + buckets.cacheWrite + buckets.output),
2005
+ tokens: (prev === void 0 ? 0 : prev.tokens) + (buckets === null ? 0 : buckets.uncached + buckets.cacheRead + buckets.cacheWrite + buckets.output),
1997
2006
  requests: (prev === void 0 ? 0 : prev.requests) + 1,
1998
2007
  ...cost !== void 0 ? { cost } : {},
1999
2008
  ...prev?.skills !== void 0 ? { skills: prev.skills } : {}
@@ -2007,7 +2016,11 @@ function createContextActivityDefinition() {
2007
2016
  apply: (state, event) => applyActivity(state, event),
2008
2017
  wire: {
2009
2018
  viewSchema: contextActivitySchema,
2010
- view: (state) => ({ days: Object.fromEntries(Object.entries(state.days).map(([k, v]) => [k, { ...v }])) })
2019
+ view: (state) => ({ days: Object.fromEntries(Object.entries(state.days).map(([k, v]) => [k, {
2020
+ ...v,
2021
+ ...v.cost !== void 0 ? { cost: copyCostUsage(v.cost) } : {},
2022
+ ...v.skills !== void 0 ? { skills: Object.fromEntries(Object.entries(v.skills).map(([n, t]) => [n, { ...t }])) } : {}
2023
+ }])) })
2011
2024
  },
2012
2025
  stateVersion: 4
2013
2026
  };
@@ -2172,19 +2185,36 @@ function createToolAttribution(ctx) {
2172
2185
  return { ownerOf: (name) => mcpSourceOf(name) ?? live.get(name) ?? pinnedSourceOf(name) ?? (boot.has(name) ? "<unknown-plugin>" : void 0) };
2173
2186
  }
2174
2187
  //#endregion
2188
+ //#region src/host/record.ts
2189
+ function asRecord$1(value) {
2190
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2191
+ }
2192
+ //#endregion
2175
2193
  //#region src/host/connection.ts
2176
2194
  function fetchRouteRegistrar(connection) {
2177
2195
  return typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
2178
2196
  }
2197
+ function registerPostRoute(c, connection, path, fetch, label) {
2198
+ const register = fetchRouteRegistrar(connection);
2199
+ if (register === void 0) return false;
2200
+ try {
2201
+ c.effect(() => register({
2202
+ path,
2203
+ methods: ["POST"],
2204
+ requestBody: "buffered",
2205
+ fetch
2206
+ }), label);
2207
+ } catch {
2208
+ return false;
2209
+ }
2210
+ return true;
2211
+ }
2179
2212
  //#endregion
2180
2213
  //#region src/host/balance.ts
2181
2214
  const BALANCE_ROUTE = "/api/dsh-context/balance";
2182
2215
  const DEEPSEEK_SETTINGS_NS = "llm-deepseek";
2183
2216
  const PUBLIC_BASE_URL = "https://api.deepseek.com";
2184
2217
  const FETCH_TIMEOUT_MS = 1e4;
2185
- function asRecord$2(value) {
2186
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2187
- }
2188
2218
  function amountOf(value) {
2189
2219
  const n = typeof value === "string" ? Number(value) : value;
2190
2220
  return typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null;
@@ -2197,9 +2227,9 @@ function deepseekSectionOf(ctx) {
2197
2227
  if (!Array.isArray(rows)) return null;
2198
2228
  let shaped = null;
2199
2229
  for (const row of rows) try {
2200
- const record = asRecord$2(row);
2230
+ const record = asRecord$1(row);
2201
2231
  if (record === null) continue;
2202
- const value = asRecord$2(record.value);
2232
+ const value = asRecord$1(record.value);
2203
2233
  if (value === null || typeof value.apiKeyEnv !== "string" || value.apiKeyEnv === "") continue;
2204
2234
  if (record.ns === DEEPSEEK_SETTINGS_NS || record.ns === "llm-deepseek-api-key") return value;
2205
2235
  shaped ??= value;
@@ -2232,12 +2262,12 @@ async function resolveFacts(ctx) {
2232
2262
  };
2233
2263
  }
2234
2264
  function balanceOfPayload(value) {
2235
- const data = asRecord$2(value);
2265
+ const data = asRecord$1(value);
2236
2266
  if (data === null) return null;
2237
2267
  const infos = Array.isArray(data.balance_infos) ? data.balance_infos : [];
2238
2268
  const balances = [];
2239
2269
  for (const info of infos) try {
2240
- const entry = asRecord$2(info);
2270
+ const entry = asRecord$1(info);
2241
2271
  if (entry === null) continue;
2242
2272
  const currency = entry.currency;
2243
2273
  const granted = amountOf(entry.granted_balance);
@@ -2285,8 +2315,6 @@ async function readBalance(facts) {
2285
2315
  }
2286
2316
  function watchBalanceChannel(ctx) {
2287
2317
  ctx.inject(["connection"], (c) => {
2288
- const register = fetchRouteRegistrar(c.get("connection"));
2289
- if (register === void 0) return;
2290
2318
  const handler = async () => {
2291
2319
  const facts = await resolveFacts(ctx);
2292
2320
  const value = facts !== null ? await readBalance(facts) : null;
@@ -2295,16 +2323,7 @@ function watchBalanceChannel(ctx) {
2295
2323
  value
2296
2324
  }, { headers: { "cache-control": "no-store" } });
2297
2325
  };
2298
- try {
2299
- c.effect(() => register({
2300
- path: BALANCE_ROUTE,
2301
- methods: ["POST"],
2302
- requestBody: "buffered",
2303
- fetch: handler
2304
- }), "dsh-context: balance route");
2305
- } catch {
2306
- return;
2307
- }
2326
+ registerPostRoute(c, c.get("connection"), BALANCE_ROUTE, handler, "dsh-context: balance route");
2308
2327
  });
2309
2328
  }
2310
2329
  //#endregion
@@ -2340,9 +2359,6 @@ function makeColdReadGate(reading = v8Reading) {
2340
2359
  const BACKFILL_ROUTE = "/api/dsh-context/backfill";
2341
2360
  const YIELD_MS = 100;
2342
2361
  const UNSUPPORTED_FORMAT = "SessionFormatUnsupported";
2343
- function asRecord$1(value) {
2344
- return value !== null && typeof value === "object" ? value : null;
2345
- }
2346
2362
  function headerOf(record) {
2347
2363
  const header = asRecord$1(asRecord$1(record)?.header);
2348
2364
  if (header === null) return null;
@@ -2392,7 +2408,7 @@ function isUnsupportedFormat(error) {
2392
2408
  return false;
2393
2409
  }
2394
2410
  }
2395
- function messageOf(error) {
2411
+ function messageOf$1(error) {
2396
2412
  try {
2397
2413
  return String(error);
2398
2414
  } catch {
@@ -2409,22 +2425,12 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
2409
2425
  launch();
2410
2426
  };
2411
2427
  const routeFiber = ctx.inject(["connection"], (c) => {
2412
- const register = fetchRouteRegistrar(c.get("connection"));
2413
- if (register === void 0) return;
2414
- try {
2415
- c.effect(() => register({
2416
- path: BACKFILL_ROUTE,
2417
- methods: ["POST"],
2418
- requestBody: "buffered",
2419
- fetch: () => {
2420
- requested = true;
2421
- tryLaunch();
2422
- return Response.json({ ok: true }, { headers: { "cache-control": "no-store" } });
2423
- }
2424
- }), "dsh-context: backfill route");
2425
- } catch {
2426
- return;
2427
- }
2428
+ const handler = () => {
2429
+ requested = true;
2430
+ tryLaunch();
2431
+ return Response.json({ ok: true }, { headers: { "cache-control": "no-store" } });
2432
+ };
2433
+ registerPostRoute(c, c.get("connection"), BACKFILL_ROUTE, handler, "dsh-context: backfill route");
2428
2434
  });
2429
2435
  const runnerFiber = ctx.inject([
2430
2436
  "sessionQuery",
@@ -2466,8 +2472,8 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
2466
2472
  } catch (error) {
2467
2473
  if (isUnsupportedFormat(error)) {
2468
2474
  refused++;
2469
- ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
2470
- } else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
2475
+ ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf$1(error)})`);
2476
+ } else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf$1(error)})`);
2471
2477
  }
2472
2478
  await new Promise((resolve) => setTimeout(resolve, YIELD_MS));
2473
2479
  }
@@ -2477,7 +2483,7 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
2477
2483
  };
2478
2484
  launch = () => {
2479
2485
  run().catch((error) => {
2480
- if (!abort.signal.aborted) ctx.logger.warn(`dsh-context: projection backfill stopped early (${messageOf(error)})`);
2486
+ if (!abort.signal.aborted) ctx.logger.warn(`dsh-context: projection backfill stopped early (${messageOf$1(error)})`);
2481
2487
  });
2482
2488
  };
2483
2489
  tryLaunch();
@@ -2489,14 +2495,11 @@ function watchActivityBackfill(ctx, coldReads = makeColdReadGate()) {
2489
2495
  for (const fiber of [routeFiber, runnerFiber]) fiber?.dispose?.();
2490
2496
  };
2491
2497
  }
2492
- //#endregion
2493
- //#region src/host/detail.ts
2494
- const DETAIL_ROUTE = "/api/dsh-context/detail";
2495
- function reply(value) {
2498
+ function reply$1(value) {
2496
2499
  return Response.json(value, { headers: { "cache-control": "no-store" } });
2497
2500
  }
2498
- function failure(code, message) {
2499
- return reply({
2501
+ function failure$1(code, message) {
2502
+ return reply$1({
2500
2503
  ok: false,
2501
2504
  error: {
2502
2505
  code,
@@ -2508,9 +2511,8 @@ function watchDetailChannel(ctx, bounds, coldReads = makeColdReadGate()) {
2508
2511
  const gate = { live: false };
2509
2512
  ctx.inject(["connection", "sessions"], (c) => {
2510
2513
  const sessions = c.get("sessions");
2511
- const register = fetchRouteRegistrar(c.get("connection"));
2512
2514
  const getSession = typeof sessions?.get === "function" ? sessions.get.bind(sessions) : void 0;
2513
- if (register === void 0 || getSession === void 0) return;
2515
+ if (getSession === void 0) return;
2514
2516
  const projections = ctx.sessionProjections;
2515
2517
  const handler = async (request) => {
2516
2518
  let sessionId;
@@ -2518,29 +2520,29 @@ function watchDetailChannel(ctx, bounds, coldReads = makeColdReadGate()) {
2518
2520
  const body = await request.json();
2519
2521
  sessionId = body !== null && typeof body === "object" ? body.sessionId : void 0;
2520
2522
  } catch {
2521
- return failure("dsh-context/bad-request", "body is not JSON");
2523
+ return failure$1("dsh-context/bad-request", "body is not JSON");
2522
2524
  }
2523
- if (typeof sessionId !== "string" || sessionId === "") return failure("dsh-context/bad-request", "missing sessionId");
2525
+ if (typeof sessionId !== "string" || sessionId === "") return failure$1("dsh-context/bad-request", "missing sessionId");
2524
2526
  try {
2525
2527
  const session = getSession(sessionId);
2526
2528
  if (session !== void 0 && session !== null) {
2527
2529
  const state = projections.stateOf(session, "contextTimeline");
2528
- if (state === void 0) return reply({
2530
+ if (state === void 0) return reply$1({
2529
2531
  ok: true,
2530
2532
  value: null
2531
2533
  });
2532
- return reply({
2534
+ return reply$1({
2533
2535
  ok: true,
2534
2536
  value: buildTimelineDetail(state, bounds)
2535
2537
  });
2536
2538
  }
2537
2539
  const query = ctx.get("sessionQuery");
2538
2540
  const observe = typeof query?.observeSession === "function" ? query.observeSession.bind(query) : void 0;
2539
- if (observe === void 0) return reply({
2541
+ if (observe === void 0) return reply$1({
2540
2542
  ok: true,
2541
2543
  value: null
2542
2544
  });
2543
- return reply({
2545
+ return reply$1({
2544
2546
  ok: true,
2545
2547
  value: await coldReads.admit(async () => {
2546
2548
  const observation = await observe(sessionId, { projectionMode: "none" });
@@ -2557,19 +2559,10 @@ function watchDetailChannel(ctx, bounds, coldReads = makeColdReadGate()) {
2557
2559
  }) ?? null
2558
2560
  });
2559
2561
  } catch (err) {
2560
- return failure("gateway/internal", err instanceof Error ? err.message : String(err));
2562
+ return failure$1("gateway/internal", err instanceof Error ? err.message : String(err));
2561
2563
  }
2562
2564
  };
2563
- try {
2564
- c.effect(() => register({
2565
- path: DETAIL_ROUTE,
2566
- methods: ["POST"],
2567
- requestBody: "buffered",
2568
- fetch: handler
2569
- }), "dsh-context: detail route");
2570
- } catch {
2571
- return;
2572
- }
2565
+ if (!registerPostRoute(c, c.get("connection"), "/api/dsh-context/detail", handler, "dsh-context: detail route")) return;
2573
2566
  gate.live = true;
2574
2567
  return () => {
2575
2568
  gate.live = false;
@@ -2746,6 +2739,237 @@ function createFallbackActivityDefinition() {
2746
2739
  return fallbackDefinition("contextActivity", contextActivitySchema, { days: {} });
2747
2740
  }
2748
2741
  //#endregion
2742
+ //#region src/host/fleet.ts
2743
+ const FLEET_ROUTE = "/api/dsh-context/fleet";
2744
+ const MESSAGES_MAX = 40;
2745
+ const MESSAGE_TEXT_MAX = 400;
2746
+ const PROMPT_TEXT_MAX = 1600;
2747
+ const REPLY_TEXT_MAX = 300;
2748
+ function reply(value) {
2749
+ return Response.json(value, { headers: { "cache-control": "no-store" } });
2750
+ }
2751
+ function failure(code, message) {
2752
+ return reply({
2753
+ ok: false,
2754
+ error: {
2755
+ code,
2756
+ message
2757
+ }
2758
+ });
2759
+ }
2760
+ function asRecord(value) {
2761
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2762
+ }
2763
+ function textOfContent(content) {
2764
+ if (!Array.isArray(content)) return "";
2765
+ let out = "";
2766
+ for (const block of content) {
2767
+ const text = asRecord(block)?.text;
2768
+ if (typeof text === "string") out += text;
2769
+ }
2770
+ return out;
2771
+ }
2772
+ const REMINDER_PREFIX = /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*/;
2773
+ function clip(text, max) {
2774
+ return text.length > max ? {
2775
+ text: text.slice(0, max) + "…",
2776
+ cut: true
2777
+ } : {
2778
+ text,
2779
+ cut: false
2780
+ };
2781
+ }
2782
+ function descriptorOf(data) {
2783
+ const mode = data.mode;
2784
+ if (mode !== "one-shot" && mode !== "continuable") return null;
2785
+ const out = { mode };
2786
+ for (const key of [
2787
+ "label",
2788
+ "provider",
2789
+ "agentProvider",
2790
+ "agentModel",
2791
+ "persona"
2792
+ ]) {
2793
+ const value = data[key];
2794
+ if (typeof value === "string" && value !== "") out[key] = value;
2795
+ }
2796
+ return out;
2797
+ }
2798
+ const MAILBOX_PREFIX = /^Team message \S+ from [^:]{1,64}:\s*/;
2799
+ const RELAY_PREFIX = /^(?:Agent \S+ sent a message: |Team message from [^:\n]{1,64}:\s*)/;
2800
+ function messageOf(seq, time, data) {
2801
+ const source = asRecord(data.source);
2802
+ const kind = source?.kind;
2803
+ let message = null;
2804
+ if (kind === "agent-message" || kind === "team-message") {
2805
+ let text = textOfContent(data.content);
2806
+ text = text.replace(kind === "team-message" ? MAILBOX_PREFIX : RELAY_PREFIX, "");
2807
+ if (text.trim() === "") return null;
2808
+ message = {
2809
+ seq,
2810
+ time,
2811
+ kind: kind === "agent-message" ? "relay" : "mailbox",
2812
+ text
2813
+ };
2814
+ const from = kind === "agent-message" ? source?.senderSessionId : source?.senderId;
2815
+ if (typeof from === "string" && from !== "") message.from = from;
2816
+ const fromName = source?.senderName;
2817
+ if (typeof fromName === "string" && fromName !== "") message.fromName = fromName;
2818
+ } else if (kind === "subagent-settled") {
2819
+ const summary = source?.summary;
2820
+ const text = typeof summary === "string" && summary.trim() !== "" ? summary : textOfContent(data.content);
2821
+ if (text.trim() === "") return null;
2822
+ message = {
2823
+ seq,
2824
+ time,
2825
+ kind: "settled",
2826
+ text
2827
+ };
2828
+ const from = source?.senderSessionId;
2829
+ if (typeof from === "string" && from !== "") message.from = from;
2830
+ }
2831
+ if (message === null) return null;
2832
+ const cut = clip(message.text, MESSAGE_TEXT_MAX);
2833
+ message.text = cut.text;
2834
+ return message;
2835
+ }
2836
+ function isPlainPrompt(source) {
2837
+ if (source === null) return true;
2838
+ return source.kind === "user";
2839
+ }
2840
+ function rosterMemberOf(data) {
2841
+ const member = asRecord(data.member);
2842
+ if (member === null) return null;
2843
+ if (typeof member.name !== "string" || member.name === "") return null;
2844
+ const out = {
2845
+ name: member.name,
2846
+ description: typeof member.description === "string" ? member.description : ""
2847
+ };
2848
+ if (typeof member.provider === "string" && member.provider !== "") out.provider = member.provider;
2849
+ if (member.context === "fresh" || member.context === "fork") out.context = member.context;
2850
+ return out;
2851
+ }
2852
+ function lastReplyOf(events) {
2853
+ for (let i = events.length - 1; i >= 0; i--) {
2854
+ const ev = asRecord(events[i]);
2855
+ if (ev === null || ev.type !== "assistant/message") continue;
2856
+ const content = asRecord(asRecord(ev.data)?.message)?.content;
2857
+ if (!Array.isArray(content)) continue;
2858
+ let text = "";
2859
+ for (const block of content) {
2860
+ const b = asRecord(block);
2861
+ if (b?.type === "text" && typeof b.text === "string") text += b.text;
2862
+ }
2863
+ if (text.trim() === "") continue;
2864
+ const time = typeof ev.time === "number" && Number.isFinite(ev.time) ? ev.time : 0;
2865
+ const cut = clip(text, REPLY_TEXT_MAX);
2866
+ return {
2867
+ time,
2868
+ text: cut.text,
2869
+ cut: cut.cut
2870
+ };
2871
+ }
2872
+ return null;
2873
+ }
2874
+ function fleetDetailOfEvents(events) {
2875
+ let descriptor;
2876
+ let initialPrompt;
2877
+ let truncated = false;
2878
+ const messages = [];
2879
+ const roster = new Map();
2880
+ for (const entry of events) {
2881
+ const ev = asRecord(entry);
2882
+ if (ev === null) continue;
2883
+ const type = ev.type;
2884
+ if (typeof type !== "string") continue;
2885
+ const seq = typeof ev.seq === "number" && Number.isFinite(ev.seq) ? ev.seq : 0;
2886
+ const time = typeof ev.time === "number" && Number.isFinite(ev.time) ? ev.time : 0;
2887
+ const data = asRecord(ev.data);
2888
+ if (data === null) continue;
2889
+ if (type === "subagent/descriptor") {
2890
+ const next = descriptorOf(data);
2891
+ if (next !== null) descriptor = next;
2892
+ continue;
2893
+ }
2894
+ if (type === "team/member") {
2895
+ const member = rosterMemberOf(data);
2896
+ if (member !== null) roster.set(member.name, member);
2897
+ continue;
2898
+ }
2899
+ if (type !== "user/message") continue;
2900
+ const message = messageOf(seq, time, data);
2901
+ if (message !== null) {
2902
+ if (message.text.endsWith("…")) truncated = true;
2903
+ messages.push(message);
2904
+ continue;
2905
+ }
2906
+ if (initialPrompt === void 0 && isPlainPrompt(asRecord(data.source))) {
2907
+ const text = textOfContent(data.content).replace(REMINDER_PREFIX, "");
2908
+ if (text.trim() !== "") {
2909
+ const cut = clip(text, PROMPT_TEXT_MAX);
2910
+ initialPrompt = cut.text;
2911
+ if (cut.cut) truncated = true;
2912
+ }
2913
+ }
2914
+ }
2915
+ messages.sort((a, b) => b.seq - a.seq);
2916
+ if (messages.length > MESSAGES_MAX) {
2917
+ messages.length = MESSAGES_MAX;
2918
+ truncated = true;
2919
+ }
2920
+ const reply = lastReplyOf(events);
2921
+ if (reply !== null && reply.cut) truncated = true;
2922
+ return {
2923
+ ...descriptor !== void 0 ? { descriptor } : {},
2924
+ ...initialPrompt !== void 0 ? { initialPrompt } : {},
2925
+ ...reply !== null ? { lastReply: {
2926
+ time: reply.time,
2927
+ text: reply.text
2928
+ } } : {},
2929
+ ...roster.size > 0 ? { roster: [...roster.values()] } : {},
2930
+ messages,
2931
+ truncated
2932
+ };
2933
+ }
2934
+ function watchFleetChannel(ctx, coldReads = makeColdReadGate()) {
2935
+ ctx.inject(["connection"], (c) => {
2936
+ const handler = async (request) => {
2937
+ let sessionId;
2938
+ try {
2939
+ sessionId = asRecord(await request.json())?.sessionId;
2940
+ } catch {
2941
+ return failure("dsh-context/bad-request", "body is not JSON");
2942
+ }
2943
+ if (typeof sessionId !== "string" || sessionId === "") return failure("dsh-context/bad-request", "missing sessionId");
2944
+ try {
2945
+ const query = ctx.get("sessionQuery");
2946
+ const observe = typeof query?.observeSession === "function" ? query.observeSession.bind(query) : void 0;
2947
+ if (observe === void 0) return reply({
2948
+ ok: true,
2949
+ value: null
2950
+ });
2951
+ return reply({
2952
+ ok: true,
2953
+ value: await coldReads.admit(async () => {
2954
+ const observation = await observe(sessionId, { projectionMode: "none" });
2955
+ const events = observation?.events;
2956
+ if (!Array.isArray(events)) return null;
2957
+ try {
2958
+ return fleetDetailOfEvents(events);
2959
+ } finally {
2960
+ const dispose = observation?.[Symbol.dispose];
2961
+ if (typeof dispose === "function") dispose.call(observation);
2962
+ }
2963
+ }) ?? null
2964
+ });
2965
+ } catch (err) {
2966
+ return failure("gateway/internal", err instanceof Error ? err.message : String(err));
2967
+ }
2968
+ };
2969
+ registerPostRoute(c, c.get("connection"), FLEET_ROUTE, handler, "dsh-context: fleet route");
2970
+ });
2971
+ }
2972
+ //#endregion
2749
2973
  //#region src/host/stepIdentity.ts
2750
2974
  const MINTED_ID_PREFIX = "dshctx-";
2751
2975
  function lacksId(message) {
@@ -2787,9 +3011,6 @@ function watchStepIdentity(ctx) {
2787
3011
  //#region src/host/skills.ts
2788
3012
  const SKILLS_ROUTE = "/api/dsh-context/skills";
2789
3013
  const MAX_SKILLS = 500;
2790
- function asRecord(value) {
2791
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2792
- }
2793
3014
  function stringOf(value) {
2794
3015
  return typeof value === "string" && value !== "" ? value : void 0;
2795
3016
  }
@@ -2806,7 +3027,7 @@ async function disposeOf(resource) {
2806
3027
  } catch {}
2807
3028
  }
2808
3029
  function skillInfoOf(value) {
2809
- const record = asRecord(value);
3030
+ const record = asRecord$1(value);
2810
3031
  if (record === null || typeof record.name !== "string" || record.name === "") return null;
2811
3032
  return {
2812
3033
  name: record.name,
@@ -2817,7 +3038,7 @@ function skillInfoOf(value) {
2817
3038
  }
2818
3039
  async function sessionViewOf(ctx, sessionId) {
2819
3040
  if (sessionId === void 0) return {};
2820
- const query = asRecord(ctx.get("sessionQuery"));
3041
+ const query = asRecord$1(ctx.get("sessionQuery"));
2821
3042
  if (typeof query?.observeSession !== "function") return {};
2822
3043
  let observation;
2823
3044
  try {
@@ -2826,9 +3047,9 @@ async function sessionViewOf(ctx, sessionId) {
2826
3047
  return {};
2827
3048
  }
2828
3049
  try {
2829
- const obs = asRecord(observation);
2830
- const cwd = stringOf(asRecord(obs?.header)?.cwd);
2831
- const agentPreset = stringOf(asRecord(asRecord(obs?.projections)?.values)?.agentPreset);
3050
+ const obs = asRecord$1(observation);
3051
+ const cwd = stringOf(asRecord$1(obs?.header)?.cwd);
3052
+ const agentPreset = stringOf(asRecord$1(asRecord$1(obs?.projections)?.values)?.agentPreset);
2832
3053
  return {
2833
3054
  ...cwd === void 0 ? {} : { cwd },
2834
3055
  ...agentPreset === void 0 ? {} : { agentPreset }
@@ -2838,10 +3059,10 @@ async function sessionViewOf(ctx, sessionId) {
2838
3059
  }
2839
3060
  }
2840
3061
  async function resolveRegistry(ctx, sessionId, agentPreset) {
2841
- const presets = asRecord(ctx.get("agentPresets"));
3062
+ const presets = asRecord$1(ctx.get("agentPresets"));
2842
3063
  let live;
2843
3064
  if (sessionId !== void 0) {
2844
- const agents = asRecord(ctx.get("agents"));
3065
+ const agents = asRecord$1(ctx.get("agents"));
2845
3066
  if (typeof agents?.get === "function") try {
2846
3067
  live = agents.get.call(agents, sessionId);
2847
3068
  } catch {
@@ -2849,20 +3070,20 @@ async function resolveRegistry(ctx, sessionId, agentPreset) {
2849
3070
  }
2850
3071
  }
2851
3072
  if (live !== void 0 && live !== null && typeof presets?.serviceFor === "function") {
2852
- const scoped = asRecord(presets.serviceFor.call(presets, live, "skills"));
3073
+ const scoped = asRecord$1(presets.serviceFor.call(presets, live, "skills"));
2853
3074
  if (scoped !== null && typeof scoped.list === "function") return {
2854
3075
  registry: scoped,
2855
3076
  scope: live,
2856
3077
  lease: void 0
2857
3078
  };
2858
3079
  }
2859
- const registry = asRecord(ctx.get("skills"));
3080
+ const registry = asRecord$1(ctx.get("skills"));
2860
3081
  if (registry === null || typeof registry.list !== "function") return null;
2861
3082
  let scope;
2862
3083
  let lease;
2863
3084
  if (typeof presets?.acquireScope === "function") try {
2864
3085
  lease = await presets.acquireScope.call(presets, agentPreset);
2865
- scope = asRecord(lease)?.key;
3086
+ scope = asRecord$1(lease)?.key;
2866
3087
  } catch {
2867
3088
  lease = void 0;
2868
3089
  }
@@ -2874,13 +3095,11 @@ async function resolveRegistry(ctx, sessionId, agentPreset) {
2874
3095
  }
2875
3096
  function watchSkillCatalog(ctx) {
2876
3097
  ctx.inject(["connection"], (c) => {
2877
- const register = fetchRouteRegistrar(c.get("connection"));
2878
- if (register === void 0) return;
2879
3098
  const handler = async (request) => {
2880
3099
  let value = null;
2881
3100
  let lease;
2882
3101
  try {
2883
- const body = asRecord(await request.json());
3102
+ const body = asRecord$1(await request.json());
2884
3103
  const sessionId = stringOf(body?.sessionId);
2885
3104
  const view = await sessionViewOf(ctx, sessionId);
2886
3105
  const cwd = view.cwd ?? stringOf(body?.cwd);
@@ -2908,16 +3127,7 @@ function watchSkillCatalog(ctx) {
2908
3127
  value
2909
3128
  }, { headers: { "cache-control": "no-store" } });
2910
3129
  };
2911
- try {
2912
- c.effect(() => register({
2913
- path: SKILLS_ROUTE,
2914
- methods: ["POST"],
2915
- requestBody: "buffered",
2916
- fetch: handler
2917
- }), "dsh-context: skills route");
2918
- } catch {
2919
- return;
2920
- }
3130
+ registerPostRoute(c, c.get("connection"), SKILLS_ROUTE, handler, "dsh-context: skills route");
2921
3131
  });
2922
3132
  }
2923
3133
  //#endregion
@@ -3013,8 +3223,10 @@ function detectHarnessVersion(ctx, resolveUrl = resolveRunningModule, pluginRoot
3013
3223
  const name = "dsh-context";
3014
3224
  const inject = ["sessionProjections"];
3015
3225
  function apply(ctx, config) {
3226
+ const coldReads = makeColdReadGate();
3016
3227
  watchBalanceChannel(ctx);
3017
3228
  watchSkillCatalog(ctx);
3229
+ watchFleetChannel(ctx, coldReads);
3018
3230
  const harnessVersion = detectHarnessVersion(ctx);
3019
3231
  if (harnessVersion !== void 0 && !meetsBaseline(harnessVersion)) {
3020
3232
  ctx.sessionProjections.register(createFallbackTimelineDefinition(harnessVersion));
@@ -3024,7 +3236,6 @@ function apply(ctx, config) {
3024
3236
  }
3025
3237
  const attribution = createToolAttribution(ctx);
3026
3238
  watchStepIdentity(ctx);
3027
- const coldReads = makeColdReadGate();
3028
3239
  const gate = watchDetailChannel(ctx, resolveBounds(config), coldReads);
3029
3240
  ctx.sessionProjections.register(createContextTimelineDefinition(config, () => gate.live));
3030
3241
  ctx.sessionProjections.register(createContextHeadersDefinition((name) => attribution.ownerOf(name)));