priiisk 0.1.22 → 0.1.23

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.
@@ -2,7 +2,6 @@ import { createRequire as __priiiskCreateRequire } from "node:module";
2
2
  const require = __priiiskCreateRequire(import.meta.url);
3
3
  import {
4
4
  CampAskState,
5
- CampWorkerState,
6
5
  get2,
7
6
  make2 as make4,
8
7
  modify,
@@ -11,7 +10,7 @@ import {
11
10
  tagsExhaustive,
12
11
  type,
13
12
  update as update2
14
- } from "./chunk-U2FODXMR.js";
13
+ } from "./chunk-QDMSPX34.js";
15
14
  import {
16
15
  Tag,
17
16
  TaggedError,
@@ -23,7 +22,7 @@ import {
23
22
  catchAll,
24
23
  catchAllCause,
25
24
  currentTimeMillis,
26
- equipmentDescriptionLines,
25
+ equipmentDescriptionGroups,
27
26
  fail4 as fail,
28
27
  flatMap3 as flatMap,
29
28
  forEach,
@@ -56,7 +55,7 @@ import {
56
55
  tap,
57
56
  update,
58
57
  zipRight
59
- } from "./chunk-DPNPQG2D.js";
58
+ } from "./chunk-7OL53RZ7.js";
60
59
  import "./chunk-7UPFIIZD.js";
61
60
 
62
61
  // packages/host-ui/src/backend/campUiBackend.ts
@@ -79,13 +78,30 @@ var CampUiScreen = {
79
78
  camp: "camp",
80
79
  worker: "worker"
81
80
  };
81
+ var CampUiSidebarTab = { workers: "workers", equipment: "equip" };
82
+ var CAMP_UI_SIDEBAR_TABS = [
83
+ CampUiSidebarTab.workers,
84
+ CampUiSidebarTab.equipment
85
+ ];
86
+ var nextCampUiSidebarTab = (current, delta) => {
87
+ const index = CAMP_UI_SIDEBAR_TABS.indexOf(current);
88
+ const size = CAMP_UI_SIDEBAR_TABS.length;
89
+ const next = (((index < 0 ? 0 : index) + delta) % size + size) % size;
90
+ return CAMP_UI_SIDEBAR_TABS[next] ?? CampUiSidebarTab.workers;
91
+ };
82
92
  var campUiAskBlockId = (askId) => `ask:${askId}`;
93
+ var campUiOpenedWorkerId = (state) => state.openedWorkerId;
83
94
  var makeInitialCampUiState = (snapshot, presentationNow = 0) => ({
95
+ /*
96
+ * The cursor starts on the first worker, but nobody is opened: a camp that
97
+ * opened someone by itself answered a question nobody asked and hid what a
98
+ * fresh camp has to say about itself.
99
+ */
84
100
  ...snapshot.workers[0] === void 0 ? {} : { selectedWorkerId: snapshot.workers[0].snapshot.worker.id },
85
101
  screen: CampUiScreen.camp,
86
102
  focusedSlotId: "roster",
87
103
  detailVisible: false,
88
- inventoryVisible: false,
104
+ sidebarTab: CampUiSidebarTab.workers,
89
105
  presentationNow,
90
106
  drafts: /* @__PURE__ */ new Map(),
91
107
  scrollOffsets: /* @__PURE__ */ new Map(),
@@ -110,8 +126,11 @@ var reconcileCampUiState = (current, snapshot) => {
110
126
  );
111
127
  const fallbackIndex = Math.min(Math.max(previousIndex, 0), snapshot.workers.length - 1);
112
128
  const selectedWorkerId = previousId !== void 0 && workerIds.has(previousId) ? previousId : snapshot.workers[fallbackIndex]?.snapshot.worker.id;
129
+ const previousOpenedId = current.state.openedWorkerId;
130
+ const openedWorkerId = previousOpenedId !== void 0 && workerIds.has(previousOpenedId) ? previousOpenedId : void 0;
113
131
  const {
114
132
  selectedWorkerId: _selectedWorkerId,
133
+ openedWorkerId: _openedWorkerId,
115
134
  focusedDetailBlockId: previousFocusedBlockId,
116
135
  ...stableState
117
136
  } = current.state;
@@ -127,6 +146,7 @@ var reconcileCampUiState = (current, snapshot) => {
127
146
  return {
128
147
  ...stableState,
129
148
  ...selectedWorkerId === void 0 ? {} : { selectedWorkerId },
149
+ ...openedWorkerId === void 0 ? {} : { openedWorkerId },
130
150
  ...focusedDetailBlockId === void 0 ? {} : { focusedDetailBlockId },
131
151
  screen: selectedWorkerId === void 0 ? CampUiScreen.camp : current.state.screen,
132
152
  focusedSlotId: selectedWorkerId === void 0 ? "roster" : current.state.focusedSlotId,
@@ -182,12 +202,17 @@ var makeCampUiStore = (initial, presentationNow = 0) => gen(function* () {
182
202
  ...model,
183
203
  state: {
184
204
  ...model.state,
205
+ openedWorkerId: workerId,
185
206
  selectedWorkerId: workerId,
186
207
  screen: CampUiScreen.worker
187
208
  }
188
209
  }
189
210
  ])
190
211
  ),
212
+ closeWorker: updateState((state) => {
213
+ const { openedWorkerId: _openedWorkerId, ...rest } = state;
214
+ return { ...rest, screen: CampUiScreen.camp };
215
+ }),
191
216
  activateScreen: (screen) => modifyEffect(
192
217
  modelRef,
193
218
  (model) => screen === CampUiScreen.worker && model.state.selectedWorkerId === void 0 ? fail(
@@ -211,10 +236,9 @@ var makeCampUiStore = (initial, presentationNow = 0) => gen(function* () {
211
236
  if (state.detailVisible === detailVisible) return state;
212
237
  return { ...state, detailVisible };
213
238
  }),
214
- setInventoryVisible: (inventoryVisible) => updateState((state) => {
215
- if (state.inventoryVisible === inventoryVisible) return state;
216
- return { ...state, inventoryVisible };
217
- }),
239
+ setSidebarTab: (sidebarTab) => updateState(
240
+ (state) => state.sidebarTab === sidebarTab ? state : { ...state, sidebarTab }
241
+ ),
218
242
  setDetailBlockExpanded: (blockId, expanded) => updateState((state) => ({
219
243
  ...state,
220
244
  expandedDetailBlockIds: updateCampUiStringSet(
@@ -290,6 +314,8 @@ var emptyCampUiComponent = {
290
314
  var CampUiSlot = {
291
315
  campHeader: "camp-header",
292
316
  equipment: "equipment",
317
+ sidebarTabs: "sidebar-tabs",
318
+ overview: "overview",
293
319
  resources: "resources",
294
320
  roster: "roster",
295
321
  sessionHeader: "session-header",
@@ -439,6 +465,7 @@ var makeCampUiIntentHandler = (options) => type().pipe(
439
465
  );
440
466
  }),
441
467
  OpenWorker: ({ workerId }) => options.controller.openWorker(workerId),
468
+ CloseWorker: () => options.controller.closeWorker,
442
469
  ScrollWorker: ({ workerId, delta }) => suspend(() => {
443
470
  const offset = options.readModel().state.scrollOffsets.get(workerId) ?? 0;
444
471
  return options.controller.focusDetailBlock(void 0).pipe(
@@ -460,7 +487,11 @@ var makeCampUiIntentHandler = (options) => type().pipe(
460
487
  }),
461
488
  SelectWorker: ({ workerId }) => options.controller.selectWorker(workerId),
462
489
  SetDraft: ({ workerId, draft }) => options.controller.setDraft(workerId, draft),
463
- SetInventoryVisible: ({ visible }) => options.controller.setInventoryVisible(visible),
490
+ CycleSidebarTab: ({ delta }) => suspend(
491
+ () => options.controller.setSidebarTab(
492
+ nextCampUiSidebarTab(options.readModel().state.sidebarTab, delta)
493
+ )
494
+ ),
464
495
  SetScrollOffset: ({ workerId, offset }) => options.controller.setScrollOffset(workerId, offset),
465
496
  SetTranscriptVisible: ({ visible }) => options.controller.setDetailVisible(visible),
466
497
  Steer: ({ workerId, message }) => message.trim() === "" ? _void : options.backend.steer({ workerId, message }).pipe(
@@ -522,10 +553,10 @@ var findCampUiSpatialFocusTarget = (frame, currentSlotId, focusableSlotIds, dire
522
553
 
523
554
  // packages/host-ui/src/layout/campUiLayoutPolicy.ts
524
555
  var defaultCampUiLayoutPolicy = {
525
- leftMinimumWidth: 34,
526
- rightMinimumWidth: 34,
527
- leftTrackWidthThreshold: 94,
528
- rightTrackWidthThreshold: 130,
556
+ leftMinimumWidth: 44,
557
+ rightMinimumWidth: 44,
558
+ leftTrackWidthThreshold: 90,
559
+ rightTrackWidthThreshold: 136,
529
560
  trackGapWidth: 1,
530
561
  wideMinimumHeight: 18,
531
562
  campHeaderHeight: 1,
@@ -695,7 +726,7 @@ var makeNarrowFrame = (viewport, state, policy, slots) => {
695
726
  const footerHeight = fixedRows(viewport.height, policy.footerHeight);
696
727
  const bodyHeight = Math.max(0, viewport.height - footerHeight);
697
728
  const footerY = bodyHeight;
698
- const workerScreen = state.screen === CampUiScreen.worker && state.selectedWorkerId !== void 0;
729
+ const workerScreen = state.screen === CampUiScreen.worker && campUiOpenedWorkerId(state) !== void 0;
699
730
  const [campHeight = 0, rosterHeight = 0] = workerScreen ? [] : leftSectionHeights(bodyHeight, slots);
700
731
  const sessionHeaderHeight = workerScreen ? fixedRows(bodyHeight, policy.sessionHeaderHeight) : 0;
701
732
  const inputHeight = workerScreen ? composerHeight(bodyHeight - sessionHeaderHeight, viewport.width, policy, slots) : 0;
@@ -719,12 +750,12 @@ var makeNarrowFrame = (viewport, state, policy, slots) => {
719
750
  ])
720
751
  };
721
752
  };
722
- var makeWideFrame = (viewport, policy, slots) => {
753
+ var makeWideFrame = (viewport, state, policy, slots) => {
723
754
  const campHeaderHeight = fixedRows(viewport.height, policy.campHeaderHeight);
724
755
  const footerHeight = fixedRows(viewport.height - campHeaderHeight, policy.footerHeight);
725
756
  const bodyHeight = Math.max(0, viewport.height - campHeaderHeight - footerHeight);
726
757
  const footerY = campHeaderHeight + bodyHeight;
727
- const [leftWidth = 0, leftGap = 0, centerWidth = 0, rightGap = 0, rightWidth = 0] = allocateCampUiTrackColumns(viewport.width, [
758
+ const [leftWidth = 0, leftGap = 0, centerWidth = 0] = allocateCampUiTrackColumns(viewport.width, [
728
759
  {
729
760
  kind: CampUiTrackSizeKind.min,
730
761
  minimumColumns: policy.leftMinimumWidth,
@@ -735,46 +766,60 @@ var makeWideFrame = (viewport, policy, slots) => {
735
766
  columns: policy.trackGapWidth,
736
767
  collapseBelow: policy.leftTrackWidthThreshold
737
768
  },
738
- { kind: CampUiTrackSizeKind.fill, weight: 1 },
739
- {
740
- kind: CampUiTrackSizeKind.fixed,
741
- columns: policy.trackGapWidth,
742
- collapseBelow: policy.rightTrackWidthThreshold
743
- },
744
- {
745
- kind: CampUiTrackSizeKind.min,
746
- minimumColumns: policy.rightMinimumWidth,
747
- collapseBelow: policy.rightTrackWidthThreshold
748
- }
769
+ { kind: CampUiTrackSizeKind.fill, weight: 1 }
749
770
  ]);
750
771
  const centerX2 = leftWidth + leftGap;
751
- const rightX = centerX2 + centerWidth + rightGap;
752
772
  const sessionHeaderHeight = fixedRows(bodyHeight, policy.sessionHeaderHeight);
753
773
  const inputHeight = composerHeight(bodyHeight - sessionHeaderHeight, centerWidth, policy, slots);
754
774
  const transcriptHeight = bodyHeight - sessionHeaderHeight - inputHeight;
755
775
  const bodyY = campHeaderHeight;
776
+ const opened = campUiOpenedWorkerId(state);
777
+ const sidebarTabsHeight = leftWidth === 0 ? 0 : fixedRows(bodyHeight, 1);
756
778
  return {
757
779
  mode: CampUiLayoutMode.wide,
758
780
  dividers: [],
759
781
  regions: compact([
760
782
  region(CampUiSlot.campHeader, 0, 0, viewport.width, campHeaderHeight),
761
- region(CampUiSlot.roster, 0, bodyY, leftWidth, bodyHeight),
762
- region(CampUiSlot.sessionHeader, centerX2, bodyY, centerWidth, sessionHeaderHeight),
783
+ /*
784
+ * The tab row sits under the track it belongs to and takes one line; the
785
+ * active tab is the content above it.
786
+ */
763
787
  region(
764
- CampUiSlot.transcript,
765
- centerX2,
766
- bodyY + sessionHeaderHeight,
767
- centerWidth,
768
- transcriptHeight
788
+ state.sidebarTab === CampUiSidebarTab.equipment ? CampUiSlot.equipment : CampUiSlot.roster,
789
+ 0,
790
+ bodyY,
791
+ leftWidth,
792
+ Math.max(0, bodyHeight - sidebarTabsHeight)
769
793
  ),
770
794
  region(
771
- CampUiSlot.composer,
772
- centerX2,
773
- bodyY + sessionHeaderHeight + transcriptHeight,
774
- centerWidth,
775
- inputHeight
795
+ CampUiSlot.sidebarTabs,
796
+ 0,
797
+ bodyY + Math.max(0, bodyHeight - sidebarTabsHeight),
798
+ leftWidth,
799
+ sidebarTabsHeight
776
800
  ),
777
- region(CampUiSlot.equipment, rightX, bodyY, rightWidth, bodyHeight),
801
+ /*
802
+ * With nobody open the middle answers about the camp itself — what it
803
+ * loaded and what it can reach — instead of showing an empty transcript
804
+ * above an input that has no addressee.
805
+ */
806
+ ...opened === void 0 ? [region(CampUiSlot.overview, centerX2, bodyY, centerWidth, bodyHeight)] : [
807
+ region(CampUiSlot.sessionHeader, centerX2, bodyY, centerWidth, sessionHeaderHeight),
808
+ region(
809
+ CampUiSlot.transcript,
810
+ centerX2,
811
+ bodyY + sessionHeaderHeight,
812
+ centerWidth,
813
+ transcriptHeight
814
+ ),
815
+ region(
816
+ CampUiSlot.composer,
817
+ centerX2,
818
+ bodyY + sessionHeaderHeight + transcriptHeight,
819
+ centerWidth,
820
+ inputHeight
821
+ )
822
+ ],
778
823
  /*
779
824
  * The bottom line belongs to the conversation, not to the whole width: it
780
825
  * sits under the input and answers about the work happening there. Under
@@ -792,7 +837,7 @@ var makeDefaultCampUiLayout = (policy = defaultCampUiLayoutPolicy) => ({
792
837
  width: units2(viewport.width),
793
838
  height: units2(viewport.height)
794
839
  };
795
- return normalized.height >= policy.wideMinimumHeight ? makeWideFrame(normalized, policy, slots) : makeNarrowFrame(normalized, state, policy, slots);
840
+ return normalized.height >= policy.wideMinimumHeight ? makeWideFrame(normalized, state, policy, slots) : makeNarrowFrame(normalized, state, policy, slots);
796
841
  }
797
842
  });
798
843
 
@@ -1216,7 +1261,7 @@ var makeCampUiComposerComponent = (context, tui) => {
1216
1261
  };
1217
1262
  };
1218
1263
  const selectedEditor = () => {
1219
- const workerId = context.readModel().state.selectedWorkerId;
1264
+ const workerId = campUiOpenedWorkerId(context.readModel().state);
1220
1265
  if (workerId === void 0) return void 0;
1221
1266
  const existing = editors.get(workerId);
1222
1267
  if (existing !== void 0) return existing;
@@ -1226,7 +1271,7 @@ var makeCampUiComposerComponent = (context, tui) => {
1226
1271
  };
1227
1272
  const renderSelectedEditor = (width) => {
1228
1273
  const model = context.readModel();
1229
- const workerId = model.state.selectedWorkerId;
1274
+ const workerId = campUiOpenedWorkerId(model.state);
1230
1275
  const entry = selectedEditor();
1231
1276
  if (workerId === void 0 || entry === void 0) return void 0;
1232
1277
  const { editor } = entry;
@@ -1761,7 +1806,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1761
1806
  visibleRanges = [];
1762
1807
  const model = context.readModel();
1763
1808
  const selectTheme = getSelectListTheme8();
1764
- const workerId = model.state.selectedWorkerId;
1809
+ const workerId = campUiOpenedWorkerId(model.state);
1765
1810
  if (workerId === void 0) return [selectTheme.description("No worker selected")];
1766
1811
  const worker = findCampUiWorker(model.snapshot, workerId);
1767
1812
  if (worker === void 0) return [selectTheme.noMatch("Selected worker is unavailable")];
@@ -1841,7 +1886,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1841
1886
  const delta = matchesKey2(input, Key2.up) ? -1 : matchesKey2(input, Key2.down) ? 1 : void 0;
1842
1887
  if (delta === void 0) return false;
1843
1888
  const model = context.readModel();
1844
- const workerId = model.state.selectedWorkerId;
1889
+ const workerId = campUiOpenedWorkerId(model.state);
1845
1890
  if (workerId === void 0) return false;
1846
1891
  const focusedBlockId = model.state.focusedDetailBlockId;
1847
1892
  const current = renderedRanges.findIndex((range) => range.id === focusedBlockId);
@@ -1858,7 +1903,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1858
1903
  return true;
1859
1904
  },
1860
1905
  handlePointer: (pointer) => {
1861
- const workerId = context.readModel().state.selectedWorkerId;
1906
+ const workerId = campUiOpenedWorkerId(context.readModel().state);
1862
1907
  if (workerId === void 0) return [];
1863
1908
  if (pointer.action === "wheel") {
1864
1909
  return [
@@ -1991,9 +2036,9 @@ var PULSE_WINDOW_MS = 12e4;
1991
2036
  var PULSE_CEILING = 4;
1992
2037
  var PULSE_SAMPLES_PER_COLUMN = 2;
1993
2038
  var rosterWidths = (width) => {
1994
- const right = Math.min(17, Math.max(0, width - 17));
1995
- const left = Math.min(11, Math.max(0, width - right - 6));
1996
- return { left, pulse: Math.max(0, width - left - right - 4), right };
2039
+ const right = Math.min(15, Math.max(0, width - 20));
2040
+ const left = Math.min(14, Math.max(0, width - right - 6));
2041
+ return { left, pulse: Math.max(0, width - left - right - 3), right };
1997
2042
  };
1998
2043
  var rightColumn = (text, width) => {
1999
2044
  const fitted = truncateToWidth4(text, width);
@@ -2025,23 +2070,24 @@ var usageText = (entry, width) => {
2025
2070
  const usage = entry.snapshot.session?.usage;
2026
2071
  const context = percentText(usage?.context?.percent);
2027
2072
  const cache = usage?.latestCacheHitRate === void 0 ? usage === void 0 || usage.tokens.total === 0 ? "n/a" : "n/r" : percentText(usage.latestCacheHitRate);
2028
- const cost = usage === void 0 ? "\u2014" : `$${usage.cost.toFixed(3)}`;
2029
- const full = `${context} ${cache} ${cost}`;
2030
- if (visibleWidth3(full) <= width || usage === void 0) return full;
2031
- const compactCost = `$${usage.cost.toFixed(2).replace(/^0/u, "")}`;
2032
- return `${context}${cache}${compactCost}`;
2073
+ const full = `${context} cache ${cache}`;
2074
+ return visibleWidth3(full) <= width ? full : `${context} ${cache}`;
2033
2075
  };
2034
2076
  var stateText = (state, duration, width) => {
2035
2077
  const full = `${state} ${duration}`;
2036
2078
  return visibleWidth3(full) <= width ? full : state;
2037
2079
  };
2038
- var modelText = (entry) => {
2080
+ var modelText = (entry, width) => {
2039
2081
  const model = entry.snapshot.session?.usage?.model ?? entry.snapshot.worker.equipment.model;
2040
- if (model === void 0) return "model unknown";
2041
- return model.split("/").at(-1) ?? model;
2082
+ const thinking = entry.snapshot.session?.thinkingLevel ?? entry.snapshot.worker.equipment.thinkingLevel;
2083
+ if (model === void 0) return thinking === void 0 ? "model unknown" : thinking;
2084
+ const short = model.split("/").at(-1) ?? model;
2085
+ if (thinking === void 0) return short;
2086
+ const full = `${short} ${thinking}`;
2087
+ return visibleWidth3(full) <= width ? full : short;
2042
2088
  };
2043
2089
  var cardLine = (marker, left, pulse, right, widths, width) => fitCampUiLine(
2044
- `${marker} ${fitCampUiLine(left, widths.left)} ${fitCampUiLine(pulse, widths.pulse)} ${rightColumn(right, widths.right)}`,
2090
+ `${marker} ${fitCampUiLine(left, widths.left)}${fitCampUiLine(pulse, widths.pulse)} ${rightColumn(right, widths.right)}`,
2045
2091
  width
2046
2092
  );
2047
2093
  var renderExternalControllerCard = (controllerConnected, width) => {
@@ -2068,10 +2114,10 @@ var renderExternalControllerCard = (controllerConnected, width) => {
2068
2114
  )
2069
2115
  ];
2070
2116
  };
2071
- var renderWorkerCard = (entry, selectedWorkerId, detailVisible, now, width) => {
2117
+ var renderWorkerCard = (entry, cursorWorkerId, openedWorkerId, now, width) => {
2072
2118
  const worker = entry.snapshot.worker;
2073
2119
  const session = entry.snapshot.session;
2074
- const level = worker.id !== selectedWorkerId ? CampUiLevel.resting : detailVisible ? CampUiLevel.selected : CampUiLevel.focused;
2120
+ const level = worker.id === openedWorkerId ? CampUiLevel.selected : worker.id === cursorWorkerId ? CampUiLevel.focused : CampUiLevel.resting;
2075
2121
  const roleColor = campUiRoleColor(worker.equipment.role);
2076
2122
  const widths = rosterWidths(width);
2077
2123
  const timestamps = (session?.transcript ?? []).flatMap(
@@ -2094,7 +2140,7 @@ var renderWorkerCard = (entry, selectedWorkerId, detailVisible, now, width) => {
2094
2140
  campUiLevelMarkerText(level, roleColor),
2095
2141
  campUiText(name, roleColor, level),
2096
2142
  campUiPulseText(pulse[0], level, roleColor),
2097
- neutral(modelText(entry)),
2143
+ neutral(modelText(entry, widths.right)),
2098
2144
  widths,
2099
2145
  width
2100
2146
  ),
@@ -2117,16 +2163,17 @@ var makeCampUiRoster = (context) => ({
2117
2163
  return [getSelectListTheme9().description("No workers")];
2118
2164
  }
2119
2165
  const controller = external ? renderExternalControllerCard(model.snapshot.controllerConnected, viewport.width) : [];
2120
- const workers = model.snapshot.workers.flatMap((entry) => {
2166
+ const workers = model.snapshot.workers.flatMap((entry, index) => {
2121
2167
  const candidateNow = model.state.presentationNow;
2122
2168
  const now = Number.isFinite(candidateNow) ? candidateNow : entry.snapshot.worker.updatedAt;
2123
- return renderWorkerCard(
2169
+ const card = renderWorkerCard(
2124
2170
  entry,
2125
2171
  model.state.selectedWorkerId,
2126
- model.state.detailVisible,
2172
+ campUiOpenedWorkerId(model.state),
2127
2173
  now,
2128
2174
  viewport.width
2129
2175
  );
2176
+ return index === 0 ? card : ["", ...card];
2130
2177
  });
2131
2178
  return [...controller, ...workers];
2132
2179
  },
@@ -2137,7 +2184,8 @@ var makeCampUiRoster = (context) => ({
2137
2184
  if (pointer.y < controllerRows) {
2138
2185
  return [CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster })];
2139
2186
  }
2140
- const workerIndex = Math.floor((pointer.y - controllerRows) / 2);
2187
+ const offset = pointer.y - controllerRows;
2188
+ const workerIndex = offset < 2 ? 0 : Math.floor((offset + 1) / 3);
2141
2189
  const workerId = model.snapshot.workers[workerIndex]?.snapshot.worker.id;
2142
2190
  if (workerId === void 0) return [];
2143
2191
  return [
@@ -2150,7 +2198,7 @@ var makeCampUiSessionHeader = (context) => ({
2150
2198
  render: (viewport) => {
2151
2199
  const model = context.readModel();
2152
2200
  const theme = getSelectListTheme9();
2153
- const workerId = model.state.selectedWorkerId;
2201
+ const workerId = campUiOpenedWorkerId(model.state);
2154
2202
  if (workerId === void 0) {
2155
2203
  return [fitCampUiLine(theme.description("Select a worker"), viewport.width)];
2156
2204
  }
@@ -2176,11 +2224,17 @@ import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
2176
2224
  var fullEquipmentLines = (equipment, revision, width) => {
2177
2225
  if (width <= 0) return [];
2178
2226
  const theme = getSelectListTheme10();
2179
- return equipmentDescriptionLines(equipment, revision).flatMap(
2180
- (line) => wrapTextWithAnsi(theme.description(line), width).map(
2181
- (wrapped) => fitCampUiLine(wrapped, width)
2182
- )
2183
- );
2227
+ return equipmentDescriptionGroups(equipment, revision).flatMap((group) => {
2228
+ const values = group.values.length === 0 ? ["none"] : group.values;
2229
+ return [
2230
+ fitCampUiLine(theme.description(group.label), width),
2231
+ ...values.flatMap((value, index) => {
2232
+ const branch = index === values.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
2233
+ const body = `${theme.description(branch)} ${value}`;
2234
+ return wrapTextWithAnsi(body, width).map((wrapped) => fitCampUiLine(wrapped, width));
2235
+ })
2236
+ ];
2237
+ });
2184
2238
  };
2185
2239
  var renderCampUiEquipment = (equipment, revision, width, height) => {
2186
2240
  const lines = fullEquipmentLines(equipment, revision, width);
@@ -2197,7 +2251,7 @@ var renderCampUiEquipment = (equipment, revision, width, height) => {
2197
2251
  var makeCampUiEquipment = (context) => {
2198
2252
  const selectedWorker = () => {
2199
2253
  const model = context.readModel();
2200
- const workerId = model.state.selectedWorkerId;
2254
+ const workerId = campUiOpenedWorkerId(model.state);
2201
2255
  return workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
2202
2256
  };
2203
2257
  const fullLines = (width) => {
@@ -2209,6 +2263,8 @@ var makeCampUiEquipment = (context) => {
2209
2263
  );
2210
2264
  };
2211
2265
  return {
2266
+ /* A tab of the left track takes focus like the roster does. */
2267
+ focusable: true,
2212
2268
  preferredRows: (width) => fullLines(width).length,
2213
2269
  render: (viewport) => {
2214
2270
  const worker = selectedWorker();
@@ -2237,20 +2293,17 @@ var diagnosticText = (value) => {
2237
2293
  const normalized = value?.replaceAll(/\s+/gu, " ").trim();
2238
2294
  return normalized === "" ? void 0 : normalized;
2239
2295
  };
2240
- var workerCountText = (workers) => {
2241
- if (workers.length === 0) return "no workers";
2242
- const working = workers.filter(
2243
- (worker) => worker.snapshot.worker.state === CampWorkerState.running
2244
- ).length;
2245
- const waiting = workers.filter((worker) => worker.asks.some(isPendingAsk)).length;
2246
- const parts = [
2247
- `${String(workers.length)} ${workers.length === 1 ? "worker" : "workers"}`,
2248
- ...working === 0 ? [] : [`${String(working)} working`],
2249
- ...waiting === 0 ? [] : [`${String(waiting)} waiting`]
2250
- ];
2251
- return parts.join(" \xB7 ");
2296
+ var campCostText = (workers) => {
2297
+ const reported = workers.flatMap(
2298
+ (worker) => worker.snapshot.session?.usage === void 0 ? [] : [worker.snapshot.session.usage]
2299
+ );
2300
+ if (workers.length === 0) return "";
2301
+ if (reported.length === 0) return "cost unknown";
2302
+ const total = reported.reduce((sum, usage) => sum + usage.cost, 0);
2303
+ const bound = reported.length < workers.length ? "\u2265" : "";
2304
+ return `${bound}$${total.toFixed(3)}`;
2252
2305
  };
2253
- var isPendingAsk = (ask) => ask.state === "pending";
2306
+ var workerCountText = (workers) => workers.length === 0 ? "no workers" : `${String(workers.length)} ${workers.length === 1 ? "worker" : "workers"}`;
2254
2307
  var shortenPath = (value, width) => {
2255
2308
  const available = Math.max(0, Math.floor(width));
2256
2309
  if (available <= 0) return "";
@@ -2273,9 +2326,12 @@ var makeCampUiFooter = (context) => ({
2273
2326
  const diagnostic = actionError !== void 0 ? `${renderCampUiStatus("action error", CampUiTone.error)} ${theme.description(`\xB7 ${actionError}`)}` : healthMessage !== void 0 ? styleCampUiTone(healthTone, healthMessage) : health.status === CampUiHealthStatus.ready ? "" : renderCampUiStatus(health.status, healthTone);
2274
2327
  const branch = snapshot.projectBranch === null ? "" : ` \xB7 ${snapshot.projectBranch}`;
2275
2328
  const version = `v${snapshot.hostVersion}`;
2329
+ const camp = [workerCountText(snapshot.workers), campCostText(snapshot.workers)].filter(
2330
+ (part) => part !== ""
2331
+ );
2276
2332
  const candidates = [
2277
- { right: [workerCountText(snapshot.workers), version, retry], branch },
2278
- { right: [workerCountText(snapshot.workers), version], branch },
2333
+ { right: [...camp, version, retry], branch },
2334
+ { right: [...camp, version], branch },
2279
2335
  { right: [version], branch },
2280
2336
  { right: [version], branch: "" }
2281
2337
  ];
@@ -2300,10 +2356,68 @@ var makeCampUiFooter = (context) => ({
2300
2356
  }
2301
2357
  });
2302
2358
 
2359
+ // packages/host-ui/src/pi/layout/campUiOverview.ts
2360
+ import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme12 } from "@earendil-works/pi-coding-agent";
2361
+ var resourceGroup = (label, values, reported, width) => {
2362
+ const theme = getSelectListTheme12();
2363
+ const body = reported ? values.length === 0 ? theme.description("none loaded") : values.join(", ") : theme.description("not reported");
2364
+ return [
2365
+ styleCampUiTone(values.length > 0 ? CampUiTone.attention : CampUiTone.neutral, `[${label}]`),
2366
+ ` ${body}`,
2367
+ ""
2368
+ ].map((line) => fitCampUiLine(line, width));
2369
+ };
2370
+ var mcpNames = (snapshot) => uniqueCampUiNames(
2371
+ snapshot.workers.flatMap((worker) => worker.snapshot.worker.equipment.mcp.serverNames)
2372
+ );
2373
+ var makeCampUiOverview = (context) => ({
2374
+ render: (viewport) => {
2375
+ const model = context.readModel();
2376
+ const snapshot = model.snapshot;
2377
+ const theme = getSelectListTheme12();
2378
+ const dim = getMarkdownTheme8().codeBlockBorder;
2379
+ const inventory = snapshot.resources;
2380
+ const reported = inventory.reportedWorkers > 0;
2381
+ const width = viewport.width;
2382
+ const workers = snapshot.workers.length;
2383
+ const lines = [
2384
+ fitCampUiLine(`${theme.selectedText("priiisk")} ${dim(`v${snapshot.hostVersion}`)}`, width),
2385
+ fitCampUiLine(dim("\u2190 workers \xB7 enter open \xB7 esc back here"), width),
2386
+ fitCampUiLine(
2387
+ dim(
2388
+ workers === 0 ? "\u041B\u0430\u0433\u0435\u0440\u044C \u043F\u0443\u0441\u0442. \u041E\u0440\u043A\u0435\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u043D\u0430\u043D\u0438\u043C\u0430\u0435\u0442 \u0432\u043E\u0440\u043A\u0435\u0440\u043E\u0432 \u0447\u0435\u0440\u0435\u0437 CLI: priiisk hire." : `\u0412 \u043B\u0430\u0433\u0435\u0440\u0435 ${String(workers)} ${workers === 1 ? "\u0432\u043E\u0440\u043A\u0435\u0440" : "\u0432\u043E\u0440\u043A\u0435\u0440\u0430"}. \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0441\u043B\u0435\u0432\u0430 \u0438 \u043E\u0442\u043A\u0440\u043E\u0439\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B.`
2389
+ ),
2390
+ width
2391
+ ),
2392
+ "",
2393
+ ...resourceGroup(
2394
+ "Context",
2395
+ uniqueCampUiNames(inventory.contextFiles.map((item) => item.name)),
2396
+ reported,
2397
+ width
2398
+ ),
2399
+ ...resourceGroup(
2400
+ "Extensions",
2401
+ uniqueCampUiNames(inventory.extensions.map((item) => item.name)),
2402
+ reported,
2403
+ width
2404
+ ),
2405
+ ...resourceGroup(
2406
+ "Skills",
2407
+ uniqueCampUiNames(inventory.skills.map((item) => item.name)),
2408
+ reported,
2409
+ width
2410
+ ),
2411
+ ...resourceGroup("MCP", mcpNames(snapshot), snapshot.workers.length > 0, width)
2412
+ ];
2413
+ return lines.slice(0, Math.max(0, viewport.height));
2414
+ }
2415
+ });
2416
+
2303
2417
  // packages/host-ui/src/pi/layout/campUiResources.ts
2304
- import { getSelectListTheme as getSelectListTheme12 } from "@earendil-works/pi-coding-agent";
2418
+ import { getSelectListTheme as getSelectListTheme13 } from "@earendil-works/pi-coding-agent";
2305
2419
  var renderResourceTree = (label, value, tone, width) => {
2306
- const theme = getSelectListTheme12();
2420
+ const theme = getSelectListTheme13();
2307
2421
  const lines = [
2308
2422
  theme.description(label),
2309
2423
  `${theme.description("\u2514\u2500")} ${renderCampUiStatus(value, tone)}`
@@ -2326,7 +2440,7 @@ var makeCampUiResources = (context) => ({
2326
2440
  const value = names.length === 0 ? "none loaded" : names.join(", ");
2327
2441
  return partial ? `${value} \xB7 partial` : value;
2328
2442
  };
2329
- const mcpNames = uniqueCampUiNames(equipment.flatMap((item) => item.mcp.serverNames));
2443
+ const mcpNames2 = uniqueCampUiNames(equipment.flatMap((item) => item.mcp.serverNames));
2330
2444
  const mcpHealth = snapshot.workers.flatMap((worker) => worker.snapshot.mcp);
2331
2445
  const healthNames = new Set(mcpHealth.map((item) => item.serverName));
2332
2446
  const failedMcp = mcpHealth.some(
@@ -2336,15 +2450,15 @@ var makeCampUiResources = (context) => ({
2336
2450
  (item) => item.status === "connecting" || item.status === "retry-wait"
2337
2451
  );
2338
2452
  const partialMcp = mcpHealth.some((item) => !item.catalogComplete);
2339
- const missingMcpHealth = mcpNames.some((name) => !healthNames.has(name));
2340
- const mcpTone = failedMcp ? CampUiTone.error : pendingMcp || partialMcp || missingMcpHealth ? CampUiTone.attention : mcpNames.length > 0 ? CampUiTone.success : CampUiTone.neutral;
2453
+ const missingMcpHealth = mcpNames2.some((name) => !healthNames.has(name));
2454
+ const mcpTone = failedMcp ? CampUiTone.error : pendingMcp || partialMcp || missingMcpHealth ? CampUiTone.attention : mcpNames2.length > 0 ? CampUiTone.success : CampUiTone.neutral;
2341
2455
  const mcpNotes = [
2342
2456
  ...failedMcp ? ["down"] : [],
2343
2457
  ...pendingMcp ? ["connecting"] : [],
2344
2458
  ...partialMcp ? ["catalog partial"] : [],
2345
2459
  ...missingMcpHealth ? ["health not reported"] : []
2346
2460
  ];
2347
- const mcpText = !equipmentReported ? "not reported" : mcpNames.length === 0 ? "none configured" : `${mcpNames.join(", ")}${mcpNotes.length === 0 ? "" : ` \xB7 ${mcpNotes.join(", ")}`}`;
2461
+ const mcpText = !equipmentReported ? "not reported" : mcpNames2.length === 0 ? "none configured" : `${mcpNames2.join(", ")}${mcpNotes.length === 0 ? "" : ` \xB7 ${mcpNotes.join(", ")}`}`;
2348
2462
  const contentWidth = Math.max(1, viewport.width - 2);
2349
2463
  const body = [
2350
2464
  ...renderResourceTree(
@@ -2371,6 +2485,22 @@ var makeCampUiResources = (context) => ({
2371
2485
  }
2372
2486
  });
2373
2487
 
2488
+ // packages/host-ui/src/pi/layout/campUiSidebarTabs.ts
2489
+ import { getMarkdownTheme as getMarkdownTheme9, getSelectListTheme as getSelectListTheme14 } from "@earendil-works/pi-coding-agent";
2490
+ import { visibleWidth as visibleWidth5 } from "@earendil-works/pi-tui";
2491
+ var makeCampUiSidebarTabs = (context) => ({
2492
+ render: (viewport) => {
2493
+ const model = context.readModel();
2494
+ const theme = getSelectListTheme14();
2495
+ const dim = getMarkdownTheme9().codeBlockBorder;
2496
+ const row = CAMP_UI_SIDEBAR_TABS.map(
2497
+ (tab) => tab === model.state.sidebarTab ? theme.selectedText(tab) : dim(tab)
2498
+ ).join(dim("\xB7"));
2499
+ const padding = Math.max(0, Math.floor((viewport.width - visibleWidth5(row)) / 2));
2500
+ return [fitCampUiLine(`${" ".repeat(padding)}${row}`, viewport.width)];
2501
+ }
2502
+ });
2503
+
2374
2504
  // packages/host-ui/src/pi/layout/campUiDefaultSlots.ts
2375
2505
  var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2376
2506
  {
@@ -2403,6 +2533,16 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2403
2533
  slotId: CampUiSlot.composer,
2404
2534
  mount: (context) => succeed2(makeCampUiComposerComponent(context, tui))
2405
2535
  },
2536
+ {
2537
+ id: "default-overview",
2538
+ slotId: CampUiSlot.overview,
2539
+ mount: (context) => succeed2(makeCampUiOverview(context))
2540
+ },
2541
+ {
2542
+ id: "default-sidebar-tabs",
2543
+ slotId: CampUiSlot.sidebarTabs,
2544
+ mount: (context) => succeed2(makeCampUiSidebarTabs(context))
2545
+ },
2406
2546
  {
2407
2547
  id: "default-footer",
2408
2548
  slotId: CampUiSlot.footer,
@@ -2416,17 +2556,17 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
2416
2556
  ];
2417
2557
 
2418
2558
  // packages/host-ui/src/pi/layout/campUiSection.ts
2419
- import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme13 } from "@earendil-works/pi-coding-agent";
2420
- import { visibleWidth as visibleWidth5 } from "@earendil-works/pi-tui";
2559
+ import { getMarkdownTheme as getMarkdownTheme10, getSelectListTheme as getSelectListTheme15 } from "@earendil-works/pi-coding-agent";
2560
+ import { visibleWidth as visibleWidth6 } from "@earendil-works/pi-tui";
2421
2561
  var renderHeader = (header, width) => {
2422
- const theme = getMarkdownTheme8();
2423
- const selectTheme = getSelectListTheme13();
2562
+ const theme = getMarkdownTheme10();
2563
+ const selectTheme = getSelectListTheme15();
2424
2564
  const rule2 = header.focused === true ? selectTheme.selectedPrefix : theme.codeBlockBorder;
2425
2565
  const title = header.focused === true ? selectTheme.selectedText : theme.heading;
2426
2566
  const ruleCharacter = header.focused === true ? "\u2501" : "\u2500";
2427
2567
  const detail = header.detail === void 0 ? "" : ` ${selectTheme.description(header.detail)}`;
2428
2568
  const label = `${rule2(ruleCharacter.repeat(2))} ${title(header.title)}${detail} `;
2429
- const ruleWidth = Math.max(0, width - visibleWidth5(label));
2569
+ const ruleWidth = Math.max(0, width - visibleWidth6(label));
2430
2570
  return fitCampUiLine(`${label}${rule2(ruleCharacter.repeat(ruleWidth))}`, width);
2431
2571
  };
2432
2572
  var makeCampUiSection = (options) => {
@@ -2459,13 +2599,15 @@ var makeCampUiSection = (options) => {
2459
2599
  if (options.centerContent !== true) {
2460
2600
  return [renderHeader(options.header(), viewport.width), ...body];
2461
2601
  }
2462
- const available = Math.max(0, viewport.height - 1);
2463
- const padding = Math.max(0, Math.floor((available - body.length) / 2));
2602
+ const padding = Math.max(0, Math.floor((viewport.height - body.length - 1) / 2));
2464
2603
  return [
2465
- renderHeader(options.header(), viewport.width),
2466
2604
  ...Array.from({ length: padding }, () => ""),
2605
+ renderHeader(options.header(), viewport.width),
2467
2606
  ...body,
2468
- ...Array.from({ length: Math.max(0, available - padding - body.length) }, () => "")
2607
+ ...Array.from(
2608
+ { length: Math.max(0, viewport.height - padding - body.length - 1) },
2609
+ () => ""
2610
+ )
2469
2611
  ].slice(0, viewport.height);
2470
2612
  },
2471
2613
  ...inputComponents ? {
@@ -2520,9 +2662,20 @@ var decorateDefaultCampUiSections = (context, slots) => {
2520
2662
  decorated.set(
2521
2663
  CampUiSlot.equipment,
2522
2664
  makeCampUiSection({
2523
- header: () => ({ title: "Equipment" }),
2665
+ /*
2666
+ * The worker's name lives in the section header instead of being repeated
2667
+ * inside: one line saying whose grant this is beats the same name printed
2668
+ * twice within a track that is narrow to begin with.
2669
+ */
2670
+ header: () => {
2671
+ const model = context.readModel();
2672
+ const workerId = campUiOpenedWorkerId(model.state);
2673
+ const worker = workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
2674
+ const name = worker?.snapshot.worker.alias ?? workerId?.slice(0, 8);
2675
+ return name === void 0 ? { title: "Equipment" } : { title: "Equipment", detail: name };
2676
+ },
2524
2677
  centerContent: true,
2525
- size: defaultCampUiRightSectionSizes.equipment,
2678
+ size: defaultCampUiLeftSectionSizes.workers,
2526
2679
  children: [
2527
2680
  {
2528
2681
  component: equipment,
@@ -2558,7 +2711,7 @@ import { initTheme } from "@earendil-works/pi-coding-agent";
2558
2711
  import { ProcessTerminal, TUI } from "@earendil-works/pi-tui";
2559
2712
 
2560
2713
  // packages/host-ui/src/pi/piCampUiRoot.ts
2561
- import { getMarkdownTheme as getMarkdownTheme9 } from "@earendil-works/pi-coding-agent";
2714
+ import { getMarkdownTheme as getMarkdownTheme11 } from "@earendil-works/pi-coding-agent";
2562
2715
  import { Key as Key3, matchesKey as matchesKey3 } from "@earendil-works/pi-tui";
2563
2716
 
2564
2717
  // packages/host-ui/src/pi/runtime/campUiPointerInput.ts
@@ -2687,7 +2840,7 @@ var PiCampUiRoot = class {
2687
2840
  segments.push({
2688
2841
  x: divider.x,
2689
2842
  width: 1,
2690
- text: getMarkdownTheme9().codeBlockBorder("\u2502")
2843
+ text: getMarkdownTheme11().codeBlockBorder("\u2502")
2691
2844
  });
2692
2845
  }
2693
2846
  }
@@ -2734,19 +2887,19 @@ var PiCampUiRoot = class {
2734
2887
  if (focusedSlotId !== model.state.focusedSlotId) {
2735
2888
  this.context.emit(CampUiIntents.FocusSlot({ slotId: focusedSlotId }));
2736
2889
  }
2737
- if (model.state.inventoryVisible && matchesKey3(input, Key3.escape)) {
2738
- this.context.emit(CampUiIntents.SetInventoryVisible({ visible: false }));
2739
- return;
2740
- }
2741
- if (focusedSlotId !== CampUiSlot.composer && input === "i") {
2742
- this.context.emit(CampUiIntents.SetInventoryVisible({ visible: true }));
2743
- return;
2744
- }
2745
2890
  if (this.slots.get(focusedSlotId)?.handleInput?.(input) === true) return;
2746
2891
  if (matchesKey3(input, Key3.escape)) {
2747
2892
  if (focusedSlotId === CampUiSlot.composer) {
2748
2893
  this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2749
- } else if (model.state.screen === CampUiScreen.worker) {
2894
+ return;
2895
+ }
2896
+ if (model.state.openedWorkerId !== void 0) {
2897
+ this.context.emit(CampUiIntents.CloseWorker());
2898
+ this.context.emit(CampUiIntents.FocusDetailBlock({}));
2899
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster }));
2900
+ return;
2901
+ }
2902
+ if (model.state.screen === CampUiScreen.worker) {
2750
2903
  this.context.emit(CampUiIntents.ActivateScreen({ screen: CampUiScreen.camp }));
2751
2904
  this.context.emit(CampUiIntents.FocusDetailBlock({}));
2752
2905
  this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.roster }));
@@ -2773,6 +2926,16 @@ var PiCampUiRoot = class {
2773
2926
  );
2774
2927
  return;
2775
2928
  }
2929
+ if (focusedSlotId === CampUiSlot.roster || focusedSlotId === CampUiSlot.equipment) {
2930
+ if (matchesKey3(input, Key3.left)) {
2931
+ this.context.emit(CampUiIntents.CycleSidebarTab({ delta: 1 }));
2932
+ return;
2933
+ }
2934
+ if (matchesKey3(input, Key3.right)) {
2935
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2936
+ return;
2937
+ }
2938
+ }
2776
2939
  if (focusedSlotId === CampUiSlot.roster) {
2777
2940
  const delta = matchesKey3(input, Key3.up) ? -1 : matchesKey3(input, Key3.down) ? 1 : void 0;
2778
2941
  if (delta !== void 0) {
@@ -2787,11 +2950,9 @@ var PiCampUiRoot = class {
2787
2950
  }
2788
2951
  if (matchesKey3(input, Key3.enter) && model.state.selectedWorkerId !== void 0) {
2789
2952
  this.context.emit(CampUiIntents.OpenWorker({ workerId: model.state.selectedWorkerId }));
2790
- this.context.emit(
2791
- CampUiIntents.FocusSlot({
2792
- slotId: this.mode === CampUiLayoutMode.wide ? CampUiSlot.composer : CampUiSlot.transcript
2793
- })
2794
- );
2953
+ if (this.mode !== CampUiLayoutMode.wide) {
2954
+ this.context.emit(CampUiIntents.FocusSlot({ slotId: CampUiSlot.transcript }));
2955
+ }
2795
2956
  return;
2796
2957
  }
2797
2958
  }
@@ -3005,22 +3166,6 @@ var mountPiCampUi = (options) => gen(function* () {
3005
3166
  );
3006
3167
  tui.addChild(root);
3007
3168
  tui.setFocus(root);
3008
- const inventory = slots.get(CampUiSlot.overlay);
3009
- const inventoryOverlay = inventory === void 0 ? void 0 : tui.showOverlay(
3010
- {
3011
- render: (width) => [
3012
- ...inventory.render({ width, height: Math.max(0, terminal.rows) })
3013
- ],
3014
- invalidate: () => inventory.invalidate?.()
3015
- },
3016
- {
3017
- width: "60%",
3018
- minWidth: 36,
3019
- margin: 1,
3020
- visible: () => current.state.inventoryVisible,
3021
- nonCapturing: true
3022
- }
3023
- );
3024
3169
  yield* sync(() => {
3025
3170
  terminal.setTitle(initial.displayName);
3026
3171
  terminal.clearScreen();
@@ -3037,9 +3182,6 @@ var mountPiCampUi = (options) => gen(function* () {
3037
3182
  )
3038
3183
  )
3039
3184
  );
3040
- if (inventoryOverlay !== void 0) {
3041
- yield* addFinalizer(() => sync(() => inventoryOverlay.hide()));
3042
- }
3043
3185
  yield* forkScoped(
3044
3186
  gen(function* () {
3045
3187
  for (let attempt = 0; attempt < 25; attempt += 1) {
@@ -3117,7 +3259,7 @@ var mountPiCampUi = (options) => gen(function* () {
3117
3259
  });
3118
3260
 
3119
3261
  // packages/host-ui/src/pi/run-selector/campRunSelector.ts
3120
- import { getMarkdownTheme as getMarkdownTheme10, getSelectListTheme as getSelectListTheme14, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
3262
+ import { getMarkdownTheme as getMarkdownTheme12, getSelectListTheme as getSelectListTheme16, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
3121
3263
  import {
3122
3264
  Key as Key4,
3123
3265
  matchesKey as matchesKey4,
@@ -3139,8 +3281,8 @@ var PiCampRunSelectorRoot = class {
3139
3281
  render(width) {
3140
3282
  const height = Math.max(0, this.getHeight());
3141
3283
  const viewportWidth = Math.max(0, width - 1);
3142
- const markdown = getMarkdownTheme10();
3143
- const selection = getSelectListTheme14();
3284
+ const markdown = getMarkdownTheme12();
3285
+ const selection = getSelectListTheme16();
3144
3286
  const header = `${markdown.codeBlockBorder("\u2500\u2500")} ${markdown.heading("Resume camp")} ${markdown.codeBlockBorder("\u2500".repeat(Math.max(0, viewportWidth - 16)))}`;
3145
3287
  const available = Math.max(0, height - 2);
3146
3288
  const capacity = Math.max(1, Math.floor(available / 2));
@@ -3223,6 +3365,7 @@ export {
3223
3365
  CAMP_UI_EXPLORED_COLLAPSED_PATHS,
3224
3366
  CAMP_UI_EXPLORED_TOOL_NAMES,
3225
3367
  CAMP_UI_REDUCED_MOTION_ENV,
3368
+ CAMP_UI_SIDEBAR_TABS,
3226
3369
  CAMP_UI_SUBSCRIPTION_BASE_DELAY_MS,
3227
3370
  CAMP_UI_SUBSCRIPTION_MAX_RETRIES,
3228
3371
  CampUiAssistantMessage,
@@ -3241,6 +3384,7 @@ export {
3241
3384
  CampUiRuntimeStatus,
3242
3385
  CampUiScreen,
3243
3386
  CampUiSectionSizeKind,
3387
+ CampUiSidebarTab,
3244
3388
  CampUiSlot,
3245
3389
  CampUiTone,
3246
3390
  CampUiTrackSizeKind,
@@ -3257,6 +3401,7 @@ export {
3257
3401
  campUiExploredReadPath,
3258
3402
  campUiExploredReadRange,
3259
3403
  campUiMessageSignature,
3404
+ campUiOpenedWorkerId,
3260
3405
  campUiPendingAskBlockRenderer,
3261
3406
  campUiPendingAskTargets,
3262
3407
  campUiReducedMotionFromEnv,
@@ -3293,14 +3438,17 @@ export {
3293
3438
  makeCampUiIntentHandler,
3294
3439
  makeCampUiIntentRouter,
3295
3440
  makeCampUiModel,
3441
+ makeCampUiOverview,
3296
3442
  makeCampUiResources,
3297
3443
  makeCampUiSection,
3444
+ makeCampUiSidebarTabs,
3298
3445
  makeCampUiStore,
3299
3446
  makeDefaultCampUiContributions,
3300
3447
  makeDefaultCampUiLayout,
3301
3448
  makeInitialCampUiState,
3302
3449
  mountPiCampUi,
3303
3450
  nextCampUiPendingAskTarget,
3451
+ nextCampUiSidebarTab,
3304
3452
  renderCampUiDetailBlockLayout,
3305
3453
  renderCampUiDetailBlocks,
3306
3454
  renderCampUiDirectedBlock,