priiisk 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,20 +8,22 @@ import {
8
8
  makeDoctorReport,
9
9
  resolveCampSurface,
10
10
  withCampRpc
11
- } from "./chunk-VHGA3U67.js";
11
+ } from "./chunk-F5HRZ52I.js";
12
12
  import {
13
13
  PI_RUNTIME_VERSION,
14
14
  PiModelCatalogIssueCode,
15
15
  inspectPiModelCatalog,
16
16
  makePiOperatorResources
17
- } from "./chunk-GCJD6IMI.js";
17
+ } from "./chunk-NBNWSCBX.js";
18
18
  import {
19
19
  CampHealthComponentStatus,
20
20
  CampHealthOverallStatus,
21
+ CampHealthVersionError,
21
22
  CampHostReadiness,
22
23
  CampLifecycle,
23
24
  CampManifestInvalidError,
24
25
  CampManifestVersionError,
26
+ DirectToolSandboxKind,
25
27
  FileSystem_exports,
26
28
  PRIIISK_HOST_VERSION,
27
29
  PRIIISK_PROTOCOL_PACKAGE_VERSION,
@@ -32,6 +34,8 @@ import {
32
34
  campManifestHostGeneration,
33
35
  campProjectPaths,
34
36
  campRecoveryDiagnosticFromCause,
37
+ currentDirectToolSandbox,
38
+ describeDirectToolSandbox,
35
39
  describeProjectConfigOverrides,
36
40
  findCampControllerHealthFailure,
37
41
  findCampRecoveryHealthComponent,
@@ -44,7 +48,7 @@ import {
44
48
  resolveCredentialsPath,
45
49
  resolveProjectScope,
46
50
  resolveWorkerConfigPath
47
- } from "./chunk-T6NUWP6F.js";
51
+ } from "./chunk-LIJU6Y2U.js";
48
52
  import {
49
53
  Clock_exports,
50
54
  Effect_exports,
@@ -74,6 +78,20 @@ var checkRuntime = () => {
74
78
  }
75
79
  );
76
80
  };
81
+ var checkDirectToolSandbox = () => {
82
+ const sandbox = currentDirectToolSandbox();
83
+ const active = sandbox.kind !== DirectToolSandboxKind.none;
84
+ return doctorCheck(
85
+ "tools.sandbox",
86
+ active ? DoctorCheckStatus.pass : DoctorCheckStatus.warning,
87
+ describeDirectToolSandbox(sandbox),
88
+ {
89
+ mechanism: sandbox.kind,
90
+ ...sandbox.absence === void 0 ? {} : { absence: sandbox.absence },
91
+ ...sandbox.detail === void 0 ? {} : { detail: sandbox.detail }
92
+ }
93
+ );
94
+ };
77
95
  var projectConfigDetails = (fs) => resolveProjectScope.pipe(
78
96
  Effect_exports.flatMap(
79
97
  (scope) => loadProjectWorkerConfig(fs, scope.cwd).pipe(
@@ -337,7 +355,25 @@ var durableHealthFallback = (fs, paths, project, manifest, expectedLive) => Effe
337
355
  ),
338
356
  Effect_exports.catchAll(
339
357
  (cause) => Effect_exports.succeed(
340
- doctorCheck(
358
+ /*
359
+ * A file left by an earlier runtime version is outdated evidence, not a
360
+ * broken camp: the next `camp up` replaces it. Calling it invalid turned
361
+ * every upgrade into a red doctor report (priiisk-nss.6.18). Damage and a
362
+ * mismatch under the current version stay failures.
363
+ */
364
+ cause instanceof CampHealthVersionError ? doctorCheck(
365
+ "health.durable",
366
+ expectedLive ? DoctorCheckStatus.warning : DoctorCheckStatus.skipped,
367
+ "Durable health snapshot was written by an earlier runtime version",
368
+ {
369
+ path: paths.healthPath,
370
+ stale: true,
371
+ errorCode: "unsupported_version",
372
+ expectedVersion: cause.expectedVersion,
373
+ receivedVersion: cause.receivedVersion,
374
+ recoveryAction: "camp_up_fresh"
375
+ }
376
+ ) : doctorCheck(
341
377
  "health.durable",
342
378
  DoctorCheckStatus.failure,
343
379
  "Durable health snapshot is invalid",
@@ -574,6 +610,7 @@ var checkSurface = (manifest) => (manifest !== void 0 && manifest.lifecycle !==
574
610
  var runDoctor = Effect_exports.gen(function* () {
575
611
  const fs = yield* FileSystem_exports.FileSystem;
576
612
  const runtime = checkRuntime();
613
+ const sandbox = checkDirectToolSandbox();
577
614
  const config = yield* checkConfig(fs);
578
615
  const piModels = yield* checkPiModels(fs);
579
616
  const projectResult = yield* Effect_exports.either(resolveProjectScope);
@@ -595,6 +632,7 @@ var runDoctor = Effect_exports.gen(function* () {
595
632
  runtime,
596
633
  config,
597
634
  ...piModels,
635
+ sandbox,
598
636
  surface2,
599
637
  ...skipped
600
638
  ],
@@ -614,6 +652,7 @@ var runDoctor = Effect_exports.gen(function* () {
614
652
  runtime,
615
653
  config,
616
654
  ...piModels,
655
+ sandbox,
617
656
  storage,
618
657
  manifestResult.check,
619
658
  surface,
@@ -1067,7 +1067,7 @@ var makeCampUiComposerComponent = (context, tui) => {
1067
1067
  };
1068
1068
 
1069
1069
  // packages/host-ui/src/pi/blocks/campUiDetail.ts
1070
- import { getSelectListTheme as getSelectListTheme7 } from "@earendil-works/pi-coding-agent";
1070
+ import { getSelectListTheme as getSelectListTheme8 } from "@earendil-works/pi-coding-agent";
1071
1071
  import { Key as Key2, matchesKey as matchesKey2 } from "@earendil-works/pi-tui";
1072
1072
 
1073
1073
  // packages/host-ui/src/pi/blocks/campUiEquipmentBlock.ts
@@ -1134,10 +1134,136 @@ var renderCampUiPanel = (label, body, width) => {
1134
1134
  ];
1135
1135
  };
1136
1136
 
1137
+ // packages/host-ui/src/pi/blocks/campUiExploredBlock.ts
1138
+ import { getSelectListTheme as getSelectListTheme6 } from "@earendil-works/pi-coding-agent";
1139
+ var CAMP_UI_EXPLORED_TOOL_NAMES = ["read"];
1140
+ var CAMP_UI_EXPLORED_COLLAPSED_PATHS = 8;
1141
+ var CampUiExploredStatus = {
1142
+ running: "running",
1143
+ completed: "completed",
1144
+ failed: "failed"
1145
+ };
1146
+ var isCampUiExploredTool = (toolName) => CAMP_UI_EXPLORED_TOOL_NAMES.includes(toolName);
1147
+ var stringArgument = (parameters, name) => {
1148
+ const value = parameters[name];
1149
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
1150
+ };
1151
+ var numberArgument = (parameters, name) => {
1152
+ const value = parameters[name];
1153
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1154
+ };
1155
+ var campUiExploredReadPath = (parameters) => stringArgument(parameters, "path");
1156
+ var campUiExploredReadRange = (parameters) => {
1157
+ const offset = numberArgument(parameters, "offset");
1158
+ const limit = numberArgument(parameters, "limit");
1159
+ if (offset === void 0 && limit === void 0) return void 0;
1160
+ const start = offset ?? 1;
1161
+ return limit === void 0 ? `from line ${String(start)}` : `lines ${String(start)}-${String(start + limit - 1)}`;
1162
+ };
1163
+ var statusRank = {
1164
+ completed: 0,
1165
+ running: 1,
1166
+ failed: 2
1167
+ };
1168
+ var splitPath = (path) => {
1169
+ const separator = path.lastIndexOf("/");
1170
+ if (separator < 0) return { directory: ".", name: path };
1171
+ if (separator === 0) return { directory: "/", name: path.slice(1) };
1172
+ return { directory: path.slice(0, separator), name: path.slice(separator + 1) };
1173
+ };
1174
+ var collectNodes = (reads) => {
1175
+ const byPath = /* @__PURE__ */ new Map();
1176
+ for (const read of reads) {
1177
+ const existing = byPath.get(read.path);
1178
+ const node = existing ?? (() => {
1179
+ const { directory, name } = splitPath(read.path);
1180
+ const created = {
1181
+ path: read.path,
1182
+ directory,
1183
+ name,
1184
+ reads: 0,
1185
+ status: CampUiExploredStatus.completed,
1186
+ ranges: [],
1187
+ errorMessage: void 0
1188
+ };
1189
+ byPath.set(read.path, created);
1190
+ return created;
1191
+ })();
1192
+ node.reads += 1;
1193
+ if (statusRank[read.status] > statusRank[node.status]) node.status = read.status;
1194
+ if (read.range !== void 0 && !node.ranges.includes(read.range)) node.ranges.push(read.range);
1195
+ if (read.errorMessage !== void 0 && node.errorMessage === void 0) {
1196
+ node.errorMessage = read.errorMessage;
1197
+ }
1198
+ }
1199
+ const directoryOrder = /* @__PURE__ */ new Map();
1200
+ for (const node of byPath.values()) {
1201
+ if (!directoryOrder.has(node.directory))
1202
+ directoryOrder.set(node.directory, directoryOrder.size);
1203
+ }
1204
+ return [...byPath.values()].sort(
1205
+ (left, right) => (directoryOrder.get(left.directory) ?? 0) - (directoryOrder.get(right.directory) ?? 0)
1206
+ );
1207
+ };
1208
+ var toneByStatus = {
1209
+ completed: CampUiTone.success,
1210
+ running: CampUiTone.active,
1211
+ failed: CampUiTone.error
1212
+ };
1213
+ var firstLine = (message) => message.split("\n")[0] ?? message;
1214
+ var nodeLine = (node, expanded) => {
1215
+ const theme = getSelectListTheme6();
1216
+ const repeats = node.reads > 1 ? theme.description(` \xD7${String(node.reads)}`) : "";
1217
+ const ranges = expanded && node.ranges.length > 0 ? theme.description(` \xB7 ${node.ranges.join(", ")}`) : "";
1218
+ const error = node.errorMessage === void 0 ? "" : theme.noMatch(` \u2014 ${firstLine(node.errorMessage)}`);
1219
+ return ` ${renderCampUiStatus(node.name, toneByStatus[node.status])}${repeats}${ranges}${error}`;
1220
+ };
1221
+ var selectVisibleNodes = (nodes, budget) => {
1222
+ if (nodes.length <= budget) return nodes;
1223
+ const attention = nodes.filter((node) => node.status !== CampUiExploredStatus.completed);
1224
+ const remaining = Math.max(0, budget - attention.length);
1225
+ const completed = nodes.filter((node) => node.status === CampUiExploredStatus.completed).slice(0, remaining);
1226
+ const keep = /* @__PURE__ */ new Set([...attention, ...completed]);
1227
+ return nodes.filter((node) => keep.has(node));
1228
+ };
1229
+ var renderCampUiExploredGroup = (reads, width, expanded) => {
1230
+ const theme = getSelectListTheme6();
1231
+ const nodes = collectNodes(reads);
1232
+ const visible = expanded ? nodes : selectVisibleNodes(nodes, CAMP_UI_EXPLORED_COLLAPSED_PATHS);
1233
+ const hidden = nodes.length - visible.length;
1234
+ const running = nodes.filter((node) => node.status === CampUiExploredStatus.running).length;
1235
+ const failed = nodes.filter((node) => node.status === CampUiExploredStatus.failed).length;
1236
+ const label = [
1237
+ `explored \xB7 ${String(nodes.length)} ${nodes.length === 1 ? "file" : "files"}`,
1238
+ ...running === 0 ? [] : [`${String(running)} running`],
1239
+ ...failed === 0 ? [] : [`${String(failed)} failed`]
1240
+ ].join(" \xB7 ");
1241
+ const innerWidth = Math.max(1, width - 2);
1242
+ const body = [];
1243
+ let directory;
1244
+ for (const node of visible) {
1245
+ if (node.directory !== directory) {
1246
+ directory = node.directory;
1247
+ body.push(theme.description(directory));
1248
+ }
1249
+ body.push(nodeLine(node, expanded));
1250
+ }
1251
+ body.push(
1252
+ theme.description(
1253
+ hidden > 0 ? `... ${String(hidden)} more paths \xB7 enter expand` : expanded ? "enter collapse" : "enter expand"
1254
+ )
1255
+ );
1256
+ return renderCampUiPanel(
1257
+ label,
1258
+ body.map((line) => fitCampUiLine(line, innerWidth)),
1259
+ width
1260
+ );
1261
+ };
1262
+
1137
1263
  // packages/host-ui/src/pi/blocks/campUiToolResult.ts
1138
1264
  import {
1139
1265
  getMarkdownTheme as getMarkdownTheme6,
1140
- getSelectListTheme as getSelectListTheme6,
1266
+ getSelectListTheme as getSelectListTheme7,
1141
1267
  truncateToVisualLines
1142
1268
  } from "@earendil-works/pi-coding-agent";
1143
1269
  import { Markdown as Markdown3 } from "@earendil-works/pi-tui";
@@ -1174,7 +1300,7 @@ var renderCampUiToolResult = (result, width, expanded) => {
1174
1300
  const boundedWidth = Math.max(1, width);
1175
1301
  const plainText = toolResultPlainText(result);
1176
1302
  const preview = truncateToVisualLines(
1177
- getSelectListTheme6().description(plainText),
1303
+ getSelectListTheme7().description(plainText),
1178
1304
  previewLinesByTool.get(result.toolName ?? "") ?? CAMP_UI_DEFAULT_TOOL_PREVIEW_LINES,
1179
1305
  boundedWidth
1180
1306
  );
@@ -1182,7 +1308,7 @@ var renderCampUiToolResult = (result, width, expanded) => {
1182
1308
  return [
1183
1309
  ...preview.visualLines,
1184
1310
  ...preview.skippedCount === 0 ? [] : [
1185
- getSelectListTheme6().description(
1311
+ getSelectListTheme7().description(
1186
1312
  `... ${preview.skippedCount} more lines \xB7 enter expand`
1187
1313
  )
1188
1314
  ]
@@ -1192,18 +1318,24 @@ var renderCampUiToolResult = (result, width, expanded) => {
1192
1318
  const lines = content === "" ? [] : new Markdown3(content, 0, 0, getMarkdownTheme6()).render(boundedWidth);
1193
1319
  return [
1194
1320
  ...lines,
1195
- ...result.errorMessage === void 0 ? [] : [getSelectListTheme6().noMatch(`Error: ${result.errorMessage}`)],
1321
+ ...result.errorMessage === void 0 ? [] : [getSelectListTheme7().noMatch(`Error: ${result.errorMessage}`)],
1196
1322
  ...result.toolResultDetails === void 0 ? [] : [
1197
- getSelectListTheme6().description("Details"),
1323
+ getSelectListTheme7().description("Details"),
1198
1324
  ...new Markdown3(jsonText(result.toolResultDetails), 0, 0, getMarkdownTheme6()).render(
1199
1325
  boundedWidth
1200
1326
  )
1201
1327
  ],
1202
- ...preview.skippedCount === 0 ? [] : [getSelectListTheme6().description("enter collapse")]
1328
+ ...preview.skippedCount === 0 ? [] : [getSelectListTheme7().description("enter collapse")]
1203
1329
  ];
1204
1330
  };
1205
1331
 
1206
1332
  // packages/host-ui/src/pi/blocks/campUiTranscriptBlocks.ts
1333
+ var toolResultErrorText = (result) => {
1334
+ for (const content of result.content) {
1335
+ if (content.kind === "text" && content.text.trim() !== "") return content.text;
1336
+ }
1337
+ return void 0;
1338
+ };
1207
1339
  var contentMarkdown = (message, includeToolCalls) => message.content.flatMap((content) => {
1208
1340
  if (content.kind === "text") return [content.text];
1209
1341
  if (content.kind === "thinking") {
@@ -1290,6 +1422,17 @@ var renderUnmatchedToolResult = (message, width, expanded) => {
1290
1422
  width
1291
1423
  );
1292
1424
  };
1425
+ var exploredRead = (toolCall, path, result) => {
1426
+ const range = campUiExploredReadRange(toolCall.arguments);
1427
+ const errorMessage = result?.isError === true ? result.errorMessage ?? toolResultErrorText(result) ?? "read failed" : void 0;
1428
+ return {
1429
+ toolCallId: toolCall.toolCallId,
1430
+ path,
1431
+ status: result === void 0 ? CampUiExploredStatus.running : result.isError === true ? CampUiExploredStatus.failed : CampUiExploredStatus.completed,
1432
+ ...range === void 0 ? {} : { range },
1433
+ ...errorMessage === void 0 ? {} : { errorMessage }
1434
+ };
1435
+ };
1293
1436
  var renderTranscript = (transcript, width, expandedDetailBlockIds) => {
1294
1437
  const results = /* @__PURE__ */ new Map();
1295
1438
  const callIds = /* @__PURE__ */ new Set();
@@ -1301,23 +1444,45 @@ var renderTranscript = (transcript, width, expandedDetailBlockIds) => {
1301
1444
  if (content.kind === "tool-call") callIds.add(content.toolCallId);
1302
1445
  }
1303
1446
  }
1304
- return transcript.flatMap((message, index) => {
1447
+ const blocks = [];
1448
+ let group = [];
1449
+ const closeGroup = () => {
1450
+ const first = group[0];
1451
+ if (first === void 0) return;
1452
+ const blockId = `explored:${first.toolCallId}`;
1453
+ blocks.push({
1454
+ id: blockId,
1455
+ lines: renderCampUiExploredGroup(group, width, expandedDetailBlockIds.has(blockId)),
1456
+ toggleable: true
1457
+ });
1458
+ group = [];
1459
+ };
1460
+ transcript.forEach((message, index) => {
1305
1461
  const messageId = message.id ?? String(index);
1306
1462
  if (message.role === "toolResult") {
1463
+ if (message.toolCallId !== void 0 && callIds.has(message.toolCallId)) return;
1464
+ closeGroup();
1307
1465
  const blockId = `tool-result:${messageId}`;
1308
- return message.toolCallId !== void 0 && callIds.has(message.toolCallId) ? [] : [
1309
- {
1310
- id: blockId,
1311
- lines: renderUnmatchedToolResult(message, width, expandedDetailBlockIds.has(blockId)),
1312
- toggleable: true
1313
- }
1314
- ];
1466
+ blocks.push({
1467
+ id: blockId,
1468
+ lines: renderUnmatchedToolResult(message, width, expandedDetailBlockIds.has(blockId)),
1469
+ toggleable: true
1470
+ });
1471
+ return;
1315
1472
  }
1316
- const blocks = [];
1317
1473
  const messageLines = renderMessage(message, width);
1318
- if (messageLines.length > 0) blocks.push({ id: `message:${messageId}`, lines: messageLines });
1474
+ if (messageLines.length > 0) {
1475
+ closeGroup();
1476
+ blocks.push({ id: `message:${messageId}`, lines: messageLines });
1477
+ }
1319
1478
  for (const content of message.content) {
1320
1479
  if (content.kind !== "tool-call") continue;
1480
+ const path = isCampUiExploredTool(content.toolName) ? campUiExploredReadPath(content.arguments) : void 0;
1481
+ if (path !== void 0) {
1482
+ group.push(exploredRead(content, path, results.get(content.toolCallId)));
1483
+ continue;
1484
+ }
1485
+ closeGroup();
1321
1486
  const blockId = `tool-call:${content.toolCallId}`;
1322
1487
  blocks.push({
1323
1488
  id: blockId,
@@ -1330,8 +1495,9 @@ var renderTranscript = (transcript, width, expandedDetailBlockIds) => {
1330
1495
  toggleable: results.has(content.toolCallId)
1331
1496
  });
1332
1497
  }
1333
- return blocks;
1334
1498
  });
1499
+ closeGroup();
1500
+ return blocks;
1335
1501
  };
1336
1502
  var campUiTranscriptBlockRenderer = {
1337
1503
  id: "transcript",
@@ -1361,7 +1527,7 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1361
1527
  renderedRanges = [];
1362
1528
  visibleRanges = [];
1363
1529
  const model = context.readModel();
1364
- const selectTheme = getSelectListTheme7();
1530
+ const selectTheme = getSelectListTheme8();
1365
1531
  const workerId = model.state.selectedWorkerId;
1366
1532
  if (workerId === void 0) return [selectTheme.description("No worker selected")];
1367
1533
  const worker = findCampUiWorker(model.snapshot, workerId);
@@ -1464,11 +1630,11 @@ var makeCampUiDetailComponent = (context, registry = defaultCampUiDetailBlockRen
1464
1630
  };
1465
1631
 
1466
1632
  // packages/host-ui/src/pi/layout/campUiChrome.ts
1467
- import { getSelectListTheme as getSelectListTheme8 } from "@earendil-works/pi-coding-agent";
1633
+ import { getSelectListTheme as getSelectListTheme9 } from "@earendil-works/pi-coding-agent";
1468
1634
  var makeCampUiCampHeader = (context) => ({
1469
1635
  render: (viewport) => {
1470
1636
  const snapshot = context.readModel().snapshot;
1471
- const theme = getSelectListTheme8();
1637
+ const theme = getSelectListTheme9();
1472
1638
  const mode = snapshot.placement === "external" ? "agentless" : "foreman";
1473
1639
  return [
1474
1640
  fitCampUiLine(theme.description(snapshot.displayName), viewport.width),
@@ -1482,7 +1648,7 @@ var makeCampUiCampHeader = (context) => ({
1482
1648
  var makeCampUiSeat = (context) => ({
1483
1649
  render: (viewport) => {
1484
1650
  const snapshot = context.readModel().snapshot;
1485
- const theme = getSelectListTheme8();
1651
+ const theme = getSelectListTheme9();
1486
1652
  const external = snapshot.placement === "external";
1487
1653
  const controller = snapshot.controllerConnected ? renderCampUiStatus("live", CampUiTone.success, CampUiGlyph.live) : renderCampUiStatus("lost", CampUiTone.error);
1488
1654
  return [
@@ -1497,7 +1663,7 @@ var makeCampUiRoster = (context) => ({
1497
1663
  focusable: true,
1498
1664
  render: (viewport) => {
1499
1665
  const model = context.readModel();
1500
- const theme = getSelectListTheme8();
1666
+ const theme = getSelectListTheme9();
1501
1667
  const focused = model.state.focusedSlotId === CampUiSlot.roster;
1502
1668
  if (model.snapshot.workers.length === 0) {
1503
1669
  return [theme.description("No workers")];
@@ -1538,7 +1704,7 @@ var makeCampUiRoster = (context) => ({
1538
1704
  var makeCampUiSessionHeader = (context) => ({
1539
1705
  render: (viewport) => {
1540
1706
  const model = context.readModel();
1541
- const theme = getSelectListTheme8();
1707
+ const theme = getSelectListTheme9();
1542
1708
  const workerId = model.state.selectedWorkerId;
1543
1709
  if (workerId === void 0) {
1544
1710
  return [fitCampUiLine(theme.description("Select a worker"), viewport.width)];
@@ -1558,7 +1724,7 @@ var makeCampUiSessionHeader = (context) => ({
1558
1724
  });
1559
1725
 
1560
1726
  // packages/host-ui/src/pi/layout/campUiFooter.ts
1561
- import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme9 } from "@earendil-works/pi-coding-agent";
1727
+ import { getMarkdownTheme as getMarkdownTheme8, getSelectListTheme as getSelectListTheme10 } from "@earendil-works/pi-coding-agent";
1562
1728
  var aggregateCampUsage = (workers) => {
1563
1729
  const usages = workers.flatMap(
1564
1730
  (worker) => worker.snapshot.session?.usage === void 0 ? [] : [worker.snapshot.session.usage]
@@ -1591,7 +1757,7 @@ var diagnosticText = (value) => {
1591
1757
  var makeCampUiCampFooter = (context) => ({
1592
1758
  render: (viewport) => {
1593
1759
  const model = context.readModel();
1594
- const theme = getSelectListTheme9();
1760
+ const theme = getSelectListTheme10();
1595
1761
  const health = model.snapshot.health;
1596
1762
  const usage = aggregateUsageText(aggregateCampUsage(model.snapshot.workers));
1597
1763
  const actionError = diagnosticText(model.state.lastActionError);
@@ -1610,7 +1776,7 @@ var makeCampUiCampFooter = (context) => ({
1610
1776
  var makeCampUiSessionFooter = (context) => ({
1611
1777
  render: (viewport) => {
1612
1778
  const model = context.readModel();
1613
- const theme = getSelectListTheme9();
1779
+ const theme = getSelectListTheme10();
1614
1780
  const workerId = model.state.selectedWorkerId;
1615
1781
  const worker = workerId === void 0 ? void 0 : findCampUiWorker(model.snapshot, workerId);
1616
1782
  const usage = worker?.snapshot.session?.usage;
@@ -1635,9 +1801,9 @@ var makeCampUiSessionFooter = (context) => ({
1635
1801
  });
1636
1802
 
1637
1803
  // packages/host-ui/src/pi/layout/campUiResources.ts
1638
- import { getSelectListTheme as getSelectListTheme10 } from "@earendil-works/pi-coding-agent";
1804
+ import { getSelectListTheme as getSelectListTheme11 } from "@earendil-works/pi-coding-agent";
1639
1805
  var renderResourceTree = (label, value, tone, width) => {
1640
- const theme = getSelectListTheme10();
1806
+ const theme = getSelectListTheme11();
1641
1807
  const lines = [
1642
1808
  theme.description(label),
1643
1809
  `${theme.description("\u2514\u2500")} ${renderCampUiStatus(value, tone)}`
@@ -1752,11 +1918,11 @@ var makeDefaultCampUiContributions = (tui, detailBlocks) => [
1752
1918
  ];
1753
1919
 
1754
1920
  // packages/host-ui/src/pi/layout/campUiSection.ts
1755
- import { getMarkdownTheme as getMarkdownTheme9, getSelectListTheme as getSelectListTheme11 } from "@earendil-works/pi-coding-agent";
1921
+ import { getMarkdownTheme as getMarkdownTheme9, getSelectListTheme as getSelectListTheme12 } from "@earendil-works/pi-coding-agent";
1756
1922
  import { visibleWidth as visibleWidth3 } from "@earendil-works/pi-tui";
1757
1923
  var renderHeader = (header, width) => {
1758
1924
  const theme = getMarkdownTheme9();
1759
- const selectTheme = getSelectListTheme11();
1925
+ const selectTheme = getSelectListTheme12();
1760
1926
  const rule2 = header.focused === true ? selectTheme.selectedPrefix : theme.codeBlockBorder;
1761
1927
  const title = header.focused === true ? selectTheme.selectedText : theme.heading;
1762
1928
  const ruleCharacter = header.focused === true ? "\u2501" : "\u2500";
@@ -2366,7 +2532,7 @@ var mountPiCampUi = (options) => Effect_exports.gen(function* () {
2366
2532
  });
2367
2533
 
2368
2534
  // packages/host-ui/src/run-selector/campRunSelector.ts
2369
- import { getMarkdownTheme as getMarkdownTheme11, getSelectListTheme as getSelectListTheme12, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
2535
+ import { getMarkdownTheme as getMarkdownTheme11, getSelectListTheme as getSelectListTheme13, initTheme as initTheme2 } from "@earendil-works/pi-coding-agent";
2370
2536
  import {
2371
2537
  Key as Key4,
2372
2538
  matchesKey as matchesKey4,
@@ -2389,7 +2555,7 @@ var PiCampRunSelectorRoot = class {
2389
2555
  const height = Math.max(0, this.getHeight());
2390
2556
  const viewportWidth = Math.max(0, width - 1);
2391
2557
  const markdown = getMarkdownTheme11();
2392
- const selection = getSelectListTheme12();
2558
+ const selection = getSelectListTheme13();
2393
2559
  const header = `${markdown.codeBlockBorder("\u2500\u2500")} ${markdown.heading("Resume camp")} ${markdown.codeBlockBorder("\u2500".repeat(Math.max(0, viewportWidth - 16)))}`;
2394
2560
  const available = Math.max(0, height - 2);
2395
2561
  const capacity = Math.max(1, Math.floor(available / 2));
@@ -2524,6 +2690,13 @@ export {
2524
2690
  makeCampUiComposerComponent,
2525
2691
  campUiEquipmentBlockRenderer,
2526
2692
  renderCampUiPanel,
2693
+ CAMP_UI_EXPLORED_TOOL_NAMES,
2694
+ CAMP_UI_EXPLORED_COLLAPSED_PATHS,
2695
+ CampUiExploredStatus,
2696
+ isCampUiExploredTool,
2697
+ campUiExploredReadPath,
2698
+ campUiExploredReadRange,
2699
+ renderCampUiExploredGroup,
2527
2700
  CAMP_UI_DEFAULT_TOOL_PREVIEW_LINES,
2528
2701
  CAMP_UI_BASH_PREVIEW_LINES,
2529
2702
  renderCampUiToolResult,
@@ -20,7 +20,7 @@ import {
20
20
  layerNet,
21
21
  makeCampSurfaceName,
22
22
  resolveProjectScopeSync
23
- } from "./chunk-T6NUWP6F.js";
23
+ } from "./chunk-LIJU6Y2U.js";
24
24
  import {
25
25
  Clock_exports,
26
26
  Config_exports,
@@ -111,7 +111,7 @@ var CliSurfaceSelectionError = class extends Data_exports.TaggedError("CliSurfac
111
111
  import { randomUUID } from "node:crypto";
112
112
 
113
113
  // packages/cli/src/domain/cliVersion.ts
114
- var PRIIISK_CLI_VERSION = "0.1.3";
114
+ var PRIIISK_CLI_VERSION = "0.1.5";
115
115
 
116
116
  // packages/cli/src/client/campRpcConnection.ts
117
117
  var PRIIISK_CONTROLLER_ID_ENV = "PRIIISK_CONTROLLER_ID";
@@ -3545,12 +3545,12 @@ var posixImpl = /* @__PURE__ */ Path.of({
3545
3545
  resolve,
3546
3546
  normalize(path) {
3547
3547
  if (path.length === 0) return ".";
3548
- const isAbsolute = path.charCodeAt(0) === 47;
3548
+ const isAbsolute2 = path.charCodeAt(0) === 47;
3549
3549
  const trailingSeparator = path.charCodeAt(path.length - 1) === 47;
3550
- path = normalizeStringPosix(path, !isAbsolute);
3551
- if (path.length === 0 && !isAbsolute) path = ".";
3550
+ path = normalizeStringPosix(path, !isAbsolute2);
3551
+ if (path.length === 0 && !isAbsolute2) path = ".";
3552
3552
  if (path.length > 0 && trailingSeparator) path += "/";
3553
- if (isAbsolute) return "/" + path;
3553
+ if (isAbsolute2) return "/" + path;
3554
3554
  return path;
3555
3555
  },
3556
3556
  isAbsolute(path) {
@@ -3770,9 +3770,9 @@ var posixImpl = /* @__PURE__ */ Path.of({
3770
3770
  };
3771
3771
  if (path.length === 0) return ret;
3772
3772
  let code = path.charCodeAt(0);
3773
- const isAbsolute = code === 47;
3773
+ const isAbsolute2 = code === 47;
3774
3774
  let start3;
3775
- if (isAbsolute) {
3775
+ if (isAbsolute2) {
3776
3776
  ret.root = "/";
3777
3777
  start3 = 1;
3778
3778
  } else {
@@ -3808,11 +3808,11 @@ var posixImpl = /* @__PURE__ */ Path.of({
3808
3808
  preDotState === 0 || // The (right-most) trimmed path component is exactly '..'
3809
3809
  preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
3810
3810
  if (end !== -1) {
3811
- if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);
3811
+ if (startPart === 0 && isAbsolute2) ret.base = ret.name = path.slice(1, end);
3812
3812
  else ret.base = ret.name = path.slice(startPart, end);
3813
3813
  }
3814
3814
  } else {
3815
- if (startPart === 0 && isAbsolute) {
3815
+ if (startPart === 0 && isAbsolute2) {
3816
3816
  ret.name = path.slice(1, startDot);
3817
3817
  ret.base = path.slice(1, end);
3818
3818
  } else {
@@ -3822,7 +3822,7 @@ var posixImpl = /* @__PURE__ */ Path.of({
3822
3822
  ret.ext = path.slice(startDot, end);
3823
3823
  }
3824
3824
  if (startPart > 0) ret.dir = path.slice(0, startPart - 1);
3825
- else if (isAbsolute) ret.dir = "/";
3825
+ else if (isAbsolute2) ret.dir = "/";
3826
3826
  return ret;
3827
3827
  },
3828
3828
  sep: "/",
@@ -10163,8 +10163,8 @@ var findAssetRoot = (moduleUrl, directoryName) => {
10163
10163
  };
10164
10164
  var assetRoots = /* @__PURE__ */ new Map();
10165
10165
  var resolveRuntimeAssetRoot = (moduleUrl, directoryName) => {
10166
- const cached2 = assetRoots.get(directoryName);
10167
- if (cached2 !== void 0) return cached2;
10166
+ const cached3 = assetRoots.get(directoryName);
10167
+ if (cached3 !== void 0) return cached3;
10168
10168
  const resolved = findAssetRoot(moduleUrl, directoryName);
10169
10169
  assetRoots.set(directoryName, resolved);
10170
10170
  return resolved;
@@ -10551,9 +10551,134 @@ var listWorkerModelCatalogEntries = (config) => {
10551
10551
  })).sort((left, right) => left.alias.localeCompare(right.alias));
10552
10552
  };
10553
10553
 
10554
+ // packages/config/src/sandbox/directToolSandbox.ts
10555
+ import { execFileSync } from "node:child_process";
10556
+ import { accessSync, constants } from "node:fs";
10557
+ import { delimiter, isAbsolute, join as join8 } from "node:path";
10558
+ var DirectToolSandboxKind = {
10559
+ bubblewrap: "bubblewrap",
10560
+ none: "none"
10561
+ };
10562
+ var DirectToolSandboxAbsence = {
10563
+ unsupportedPlatform: "unsupported_platform",
10564
+ binaryMissing: "binary_missing",
10565
+ probeFailed: "probe_failed",
10566
+ disabled: "disabled"
10567
+ };
10568
+ var DIRECT_TOOL_SANDBOX_ENV = "PRIIISK_DIRECT_TOOL_SANDBOX";
10569
+ var resolveExecutablePath = (bin, environment = process.env) => {
10570
+ const executable = (candidate) => {
10571
+ try {
10572
+ accessSync(candidate, constants.X_OK);
10573
+ return true;
10574
+ } catch {
10575
+ return false;
10576
+ }
10577
+ };
10578
+ if (bin.includes("/")) return isAbsolute(bin) && executable(bin) ? bin : void 0;
10579
+ for (const directory of (environment.PATH ?? "").split(delimiter)) {
10580
+ if (directory === "") continue;
10581
+ const candidate = join8(directory, bin);
10582
+ if (executable(candidate)) return candidate;
10583
+ }
10584
+ return void 0;
10585
+ };
10586
+ var sandboxArguments = (cwd, writable) => [
10587
+ "--unshare-all",
10588
+ "--ro-bind",
10589
+ "/",
10590
+ "/",
10591
+ "--dev",
10592
+ "/dev",
10593
+ "--proc",
10594
+ "/proc",
10595
+ "--tmpfs",
10596
+ "/tmp",
10597
+ ...writable ? ["--bind", cwd, cwd] : [],
10598
+ "--chdir",
10599
+ cwd,
10600
+ "--die-with-parent",
10601
+ "--new-session",
10602
+ "--"
10603
+ ];
10604
+ var probeBubblewrap = (command) => {
10605
+ try {
10606
+ execFileSync(command, [...sandboxArguments("/", false), "/bin/true"], {
10607
+ stdio: "ignore",
10608
+ timeout: 5e3
10609
+ });
10610
+ return void 0;
10611
+ } catch (cause) {
10612
+ return cause instanceof Error ? cause.message : String(cause);
10613
+ }
10614
+ };
10615
+ var cached2;
10616
+ var detectDirectToolSandbox = (environment = process.env, platform = process.platform) => {
10617
+ if (environment[DIRECT_TOOL_SANDBOX_ENV] === "0") {
10618
+ return { kind: DirectToolSandboxKind.none, absence: DirectToolSandboxAbsence.disabled };
10619
+ }
10620
+ if (platform !== "linux") {
10621
+ return {
10622
+ kind: DirectToolSandboxKind.none,
10623
+ absence: DirectToolSandboxAbsence.unsupportedPlatform,
10624
+ detail: platform
10625
+ };
10626
+ }
10627
+ const command = resolveExecutablePath("bwrap", environment);
10628
+ if (command === void 0) {
10629
+ return {
10630
+ kind: DirectToolSandboxKind.none,
10631
+ absence: DirectToolSandboxAbsence.binaryMissing,
10632
+ detail: "bwrap"
10633
+ };
10634
+ }
10635
+ const failure = probeBubblewrap(command);
10636
+ return failure === void 0 ? { kind: DirectToolSandboxKind.bubblewrap, command } : {
10637
+ kind: DirectToolSandboxKind.none,
10638
+ absence: DirectToolSandboxAbsence.probeFailed,
10639
+ detail: failure
10640
+ };
10641
+ };
10642
+ var currentDirectToolSandbox = () => {
10643
+ cached2 ??= detectDirectToolSandbox();
10644
+ return cached2;
10645
+ };
10646
+ var DIRECT_TOOL_SANDBOX_STATUS_FD = 3;
10647
+ var directToolSandboxCommand = (sandbox2, executablePath, args, options4) => sandbox2.kind === DirectToolSandboxKind.bubblewrap && sandbox2.command !== void 0 ? {
10648
+ command: sandbox2.command,
10649
+ args: [
10650
+ "--json-status-fd",
10651
+ String(DIRECT_TOOL_SANDBOX_STATUS_FD),
10652
+ ...sandboxArguments(options4.cwd, true),
10653
+ executablePath,
10654
+ ...args
10655
+ ],
10656
+ statusFd: DIRECT_TOOL_SANDBOX_STATUS_FD
10657
+ } : { command: executablePath, args };
10658
+ var parseDirectToolSandboxStatus = (status) => {
10659
+ let started = false;
10660
+ let exitCode3;
10661
+ for (const line of status.split("\n")) {
10662
+ const trimmed = line.trim();
10663
+ if (trimmed === "") continue;
10664
+ let document;
10665
+ try {
10666
+ document = JSON.parse(trimmed);
10667
+ } catch {
10668
+ continue;
10669
+ }
10670
+ if (typeof document !== "object" || document === null) continue;
10671
+ if ("child-pid" in document) started = true;
10672
+ const reported = document["exit-code"];
10673
+ if (typeof reported === "number") exitCode3 = reported;
10674
+ }
10675
+ return { started, ...exitCode3 === void 0 ? {} : { exitCode: exitCode3 } };
10676
+ };
10677
+ var describeDirectToolSandbox = (sandbox2) => sandbox2.kind === DirectToolSandboxKind.bubblewrap ? "External binaries run in a bubblewrap sandbox: read-only filesystem, writable worker directory, no network" : sandbox2.absence === DirectToolSandboxAbsence.disabled ? `External binaries run without an OS sandbox: disabled by ${DIRECT_TOOL_SANDBOX_ENV}` : sandbox2.absence === DirectToolSandboxAbsence.unsupportedPlatform ? `External binaries run without an OS sandbox: no supported mechanism on ${sandbox2.detail ?? "this platform"}` : sandbox2.absence === DirectToolSandboxAbsence.binaryMissing ? "External binaries run without an OS sandbox: bubblewrap (bwrap) is not installed" : "External binaries run without an OS sandbox: bubblewrap is present but cannot start";
10678
+
10554
10679
  // packages/protocol/src/domain/protocolVersion.ts
10555
10680
  var PRIIISK_PROTOCOL_VERSION = "11";
10556
- var PRIIISK_PROTOCOL_PACKAGE_VERSION = "0.1.3";
10681
+ var PRIIISK_PROTOCOL_PACKAGE_VERSION = "0.1.5";
10557
10682
  var ProtocolVersionSchema = Schema_exports.String.pipe(Schema_exports.minLength(1));
10558
10683
 
10559
10684
  // packages/protocol/src/domain/protocolIdentifiers.ts
@@ -13428,7 +13553,7 @@ var CampProtocol = class extends RpcGroup_exports.make(
13428
13553
  };
13429
13554
 
13430
13555
  // packages/camp-state/src/bootstrap/campHostVersion.ts
13431
- var PRIIISK_HOST_VERSION = "0.1.3";
13556
+ var PRIIISK_HOST_VERSION = "0.1.5";
13432
13557
 
13433
13558
  // packages/surface/src/domain/surfaceModel.ts
13434
13559
  var NonEmptyString7 = Schema_exports.String.pipe(Schema_exports.minLength(1));
@@ -14069,42 +14194,47 @@ var campHealthLiveness = (snapshot, now, staleAfterMs = CAMP_HEALTH_STALE_AFTER_
14069
14194
  import { randomUUID as randomUUID3 } from "node:crypto";
14070
14195
  var CampHealthPersistenceError = class extends Data_exports.TaggedError("CampHealthPersistenceError") {
14071
14196
  };
14197
+ var CampHealthVersionError = class extends Data_exports.TaggedError("CampHealthVersionError") {
14198
+ };
14072
14199
  var decodeHealth = Schema_exports.decodeUnknown(CampHealthSnapshotSchema, {
14073
14200
  onExcessProperty: "error"
14074
14201
  });
14202
+ var readError = (path, message, cause) => new CampHealthPersistenceError({ operation: "read", path, message, cause });
14203
+ var decodeCampHealth = (path, value) => {
14204
+ if (typeof value === "object" && value !== null && "schemaVersion" in value && typeof value.schemaVersion === "number" && Number.isSafeInteger(value.schemaVersion) && value.schemaVersion !== CAMP_HEALTH_SCHEMA_VERSION) {
14205
+ return Effect_exports.fail(
14206
+ new CampHealthVersionError({
14207
+ path,
14208
+ expectedVersion: CAMP_HEALTH_SCHEMA_VERSION,
14209
+ receivedVersion: value.schemaVersion,
14210
+ message: `health snapshot schemaVersion ${String(value.schemaVersion)} was written by another runtime version; this runtime writes ${String(CAMP_HEALTH_SCHEMA_VERSION)}`
14211
+ })
14212
+ );
14213
+ }
14214
+ return decodeHealth(value).pipe(
14215
+ Effect_exports.mapError(
14216
+ (cause) => readError(path, `health snapshot does not match schema: ${String(cause)}`, cause)
14217
+ )
14218
+ );
14219
+ };
14075
14220
  var loadCampHealth = (fs, path) => fs.exists(path).pipe(
14221
+ Effect_exports.mapError(
14222
+ (cause) => readError(path, `health snapshot is unavailable: ${String(cause)}`, cause)
14223
+ ),
14076
14224
  Effect_exports.flatMap(
14077
14225
  (exists) => exists ? fs.readFileString(path).pipe(
14226
+ Effect_exports.mapError(
14227
+ (cause) => readError(path, `health snapshot is unavailable: ${String(cause)}`, cause)
14228
+ ),
14078
14229
  Effect_exports.flatMap(
14079
14230
  (contents) => Effect_exports.try({
14080
14231
  try: () => JSON.parse(contents),
14081
- catch: (cause) => new CampHealthPersistenceError({
14082
- operation: "read",
14083
- path,
14084
- message: `health snapshot contains invalid JSON: ${String(cause)}`,
14085
- cause
14086
- })
14087
- })
14088
- ),
14089
- Effect_exports.flatMap(decodeHealth),
14090
- Effect_exports.mapError(
14091
- (cause) => cause instanceof CampHealthPersistenceError ? cause : new CampHealthPersistenceError({
14092
- operation: "read",
14093
- path,
14094
- message: `health snapshot is unavailable: ${String(cause)}`,
14095
- cause
14232
+ catch: (cause) => readError(path, `health snapshot contains invalid JSON: ${String(cause)}`, cause)
14096
14233
  })
14097
14234
  ),
14235
+ Effect_exports.flatMap((value) => decodeCampHealth(path, value)),
14098
14236
  Effect_exports.map(Option_exports.some)
14099
14237
  ) : Effect_exports.succeed(Option_exports.none())
14100
- ),
14101
- Effect_exports.mapError(
14102
- (cause) => cause instanceof CampHealthPersistenceError ? cause : new CampHealthPersistenceError({
14103
- operation: "read",
14104
- path,
14105
- message: `health snapshot is unavailable: ${String(cause)}`,
14106
- cause
14107
- })
14108
14238
  )
14109
14239
  );
14110
14240
  var saveCampHealth = (fs, path, snapshot) => {
@@ -14224,7 +14354,7 @@ var launchCampPrepared = (surface, request, persistIntent, handshake, persist) =
14224
14354
 
14225
14355
  // packages/camp-state/src/history/campLegacyState.ts
14226
14356
  import { randomUUID as randomUUID4 } from "node:crypto";
14227
- import { join as join8 } from "node:path";
14357
+ import { join as join9 } from "node:path";
14228
14358
  var LEGACY_CAMP_MANIFEST_VERSION = 1;
14229
14359
  var CampLegacyStateErrorCode = {
14230
14360
  invalidManifest: "legacy_manifest_invalid",
@@ -14289,7 +14419,7 @@ var moveStateFile = (fs, source, destination, message) => fs.rename(source, dest
14289
14419
  );
14290
14420
  var quarantineLegacyCampState = (fs, paths) => Effect_exports.gen(function* () {
14291
14421
  const timestamp = yield* Clock_exports.currentTimeMillis;
14292
- const directory = join8(
14422
+ const directory = join9(
14293
14423
  paths.quarantineDir,
14294
14424
  `legacy-v${String(LEGACY_CAMP_MANIFEST_VERSION)}-${String(timestamp)}-${randomUUID4()}`
14295
14425
  );
@@ -14303,8 +14433,8 @@ var quarantineLegacyCampState = (fs, paths) => Effect_exports.gen(function* () {
14303
14433
  )
14304
14434
  )
14305
14435
  );
14306
- const manifestPath = join8(directory, "camp.json");
14307
- const healthPath = join8(directory, "health.json");
14436
+ const manifestPath = join9(directory, "camp.json");
14437
+ const healthPath = join9(directory, "health.json");
14308
14438
  const hasHealth = yield* fs.exists(paths.healthPath).pipe(
14309
14439
  Effect_exports.mapError(
14310
14440
  (cause) => legacyError(
@@ -14357,7 +14487,7 @@ var quarantineLegacyCampState = (fs, paths) => Effect_exports.gen(function* () {
14357
14487
  });
14358
14488
 
14359
14489
  // packages/camp-state/src/history/campRunCatalog.ts
14360
- import { join as join9 } from "node:path";
14490
+ import { join as join10 } from "node:path";
14361
14491
 
14362
14492
  // packages/camp-state/src/history/campRunCatalogModel.ts
14363
14493
  var CampRunCatalogErrorCode = {
@@ -14447,7 +14577,7 @@ var listCampRuns = (fs, project, paths) => Effect_exports.gen(function* () {
14447
14577
  );
14448
14578
  const historical = yield* Effect_exports.forEach(
14449
14579
  entries,
14450
- (entry) => loadCampManifest(fs, join9(paths.runsDir, entry, "camp.json")).pipe(
14580
+ (entry) => loadCampManifest(fs, join10(paths.runsDir, entry, "camp.json")).pipe(
14451
14581
  Effect_exports.mapError(
14452
14582
  (cause) => catalogError(
14453
14583
  "list",
@@ -14459,7 +14589,7 @@ var listCampRuns = (fs, project, paths) => Effect_exports.gen(function* () {
14459
14589
  Effect_exports.flatMap(
14460
14590
  Option_exports.match({
14461
14591
  onNone: () => Effect_exports.succeed(void 0),
14462
- onSome: (manifest) => campRunPaths(paths, manifest.runId).manifestPath !== join9(paths.runsDir, entry, "camp.json") ? Effect_exports.fail(
14592
+ onSome: (manifest) => campRunPaths(paths, manifest.runId).manifestPath !== join10(paths.runsDir, entry, "camp.json") ? Effect_exports.fail(
14463
14593
  catalogError(
14464
14594
  "list",
14465
14595
  CampRunCatalogErrorCode.nonCanonicalRun,
@@ -14801,6 +14931,12 @@ export {
14801
14931
  resolveRolePreset,
14802
14932
  resolveHirePreset,
14803
14933
  listWorkerModelCatalogEntries,
14934
+ DirectToolSandboxKind,
14935
+ resolveExecutablePath,
14936
+ currentDirectToolSandbox,
14937
+ directToolSandboxCommand,
14938
+ parseDirectToolSandboxStatus,
14939
+ describeDirectToolSandbox,
14804
14940
  NetSocket,
14805
14941
  fromDuplex,
14806
14942
  layerNet,
@@ -14839,6 +14975,7 @@ export {
14839
14975
  findCampRecoveryHealthComponent,
14840
14976
  CAMP_HEALTH_HEARTBEAT_PERSIST_INTERVAL_MS,
14841
14977
  campHealthLiveness,
14978
+ CampHealthVersionError,
14842
14979
  loadCampHealth,
14843
14980
  saveCampHealth,
14844
14981
  LEGACY_CAMP_MANIFEST_VERSION,
@@ -3,7 +3,7 @@ const require = __priiiskCreateRequire(import.meta.url);
3
3
  import {
4
4
  FileSystem_exports,
5
5
  PRIIISK_PI_AGENT_DIR_ENV
6
- } from "./chunk-T6NUWP6F.js";
6
+ } from "./chunk-LIJU6Y2U.js";
7
7
  import {
8
8
  CampDeliveryPort,
9
9
  CampFailureCode,
@@ -28,7 +28,7 @@ import {
28
28
  makePlatform,
29
29
  makeRunMain,
30
30
  stdin
31
- } from "./chunk-T6NUWP6F.js";
31
+ } from "./chunk-LIJU6Y2U.js";
32
32
  import {
33
33
  BaseProto,
34
34
  __export,
@@ -3,6 +3,8 @@ const require = __priiiskCreateRequire(import.meta.url);
3
3
  import {
4
4
  CAMP_UI_BASH_PREVIEW_LINES,
5
5
  CAMP_UI_DEFAULT_TOOL_PREVIEW_LINES,
6
+ CAMP_UI_EXPLORED_COLLAPSED_PATHS,
7
+ CAMP_UI_EXPLORED_TOOL_NAMES,
6
8
  CAMP_UI_SUBSCRIPTION_BASE_DELAY_MS,
7
9
  CAMP_UI_SUBSCRIPTION_MAX_RETRIES,
8
10
  CampUiAssistantMessage,
@@ -11,6 +13,7 @@ import {
11
13
  CampUiCompositionError,
12
14
  CampUiContributionStrategy,
13
15
  CampUiControllerError,
16
+ CampUiExploredStatus,
14
17
  CampUiFocusDirection,
15
18
  CampUiGlyph,
16
19
  CampUiHealthStatus,
@@ -29,6 +32,8 @@ import {
29
32
  allocateCampUiSectionRows,
30
33
  campUiAskBlockId,
31
34
  campUiEquipmentBlockRenderer,
35
+ campUiExploredReadPath,
36
+ campUiExploredReadRange,
32
37
  campUiPendingAskBlockRenderer,
33
38
  campUiPendingAskTargets,
34
39
  campUiSubscriptionRetryDelay,
@@ -48,6 +53,7 @@ import {
48
53
  formatCampUiDuration,
49
54
  hasCampUiTextContent,
50
55
  hasPendingCampUiAsk,
56
+ isCampUiExploredTool,
51
57
  joinCampUiColumns,
52
58
  jsonText,
53
59
  makeCampUiComponentGroup,
@@ -66,6 +72,7 @@ import {
66
72
  renderCampUiDetailBlockLayout,
67
73
  renderCampUiDetailBlocks,
68
74
  renderCampUiDirectedBlock,
75
+ renderCampUiExploredGroup,
69
76
  renderCampUiPanel,
70
77
  renderCampUiState,
71
78
  renderCampUiStatus,
@@ -77,11 +84,13 @@ import {
77
84
  selectCampRun,
78
85
  styleCampUiTone,
79
86
  uniqueCampUiNames
80
- } from "./chunk-Z64QXV3O.js";
87
+ } from "./chunk-32R4IEGS.js";
81
88
  import "./chunk-KFSFN6L5.js";
82
89
  export {
83
90
  CAMP_UI_BASH_PREVIEW_LINES,
84
91
  CAMP_UI_DEFAULT_TOOL_PREVIEW_LINES,
92
+ CAMP_UI_EXPLORED_COLLAPSED_PATHS,
93
+ CAMP_UI_EXPLORED_TOOL_NAMES,
85
94
  CAMP_UI_SUBSCRIPTION_BASE_DELAY_MS,
86
95
  CAMP_UI_SUBSCRIPTION_MAX_RETRIES,
87
96
  CampUiAssistantMessage,
@@ -90,6 +99,7 @@ export {
90
99
  CampUiCompositionError,
91
100
  CampUiContributionStrategy,
92
101
  CampUiControllerError,
102
+ CampUiExploredStatus,
93
103
  CampUiFocusDirection,
94
104
  CampUiGlyph,
95
105
  CampUiHealthStatus,
@@ -108,6 +118,8 @@ export {
108
118
  allocateCampUiSectionRows,
109
119
  campUiAskBlockId,
110
120
  campUiEquipmentBlockRenderer,
121
+ campUiExploredReadPath,
122
+ campUiExploredReadRange,
111
123
  campUiPendingAskBlockRenderer,
112
124
  campUiPendingAskTargets,
113
125
  campUiSubscriptionRetryDelay,
@@ -127,6 +139,7 @@ export {
127
139
  formatCampUiDuration,
128
140
  hasCampUiTextContent,
129
141
  hasPendingCampUiAsk,
142
+ isCampUiExploredTool,
130
143
  joinCampUiColumns,
131
144
  jsonText,
132
145
  makeCampUiComponentGroup,
@@ -145,6 +158,7 @@ export {
145
158
  renderCampUiDetailBlockLayout,
146
159
  renderCampUiDetailBlocks,
147
160
  renderCampUiDirectedBlock,
161
+ renderCampUiExploredGroup,
148
162
  renderCampUiPanel,
149
163
  renderCampUiState,
150
164
  renderCampUiStatus,
@@ -4,14 +4,14 @@ const require = __priiiskCreateRequire(import.meta.url);
4
4
  import {
5
5
  NodeContext_exports,
6
6
  NodeRuntime_exports
7
- } from "./chunk-C7GQZ3PV.js";
7
+ } from "./chunk-Q7YCBJEO.js";
8
8
  import {
9
9
  CampUiBackendError,
10
10
  CampUiHealthStatus,
11
11
  CampUiPlacement,
12
12
  CampUiRuntimeStatus,
13
13
  mountPiCampUi
14
- } from "./chunk-Z64QXV3O.js";
14
+ } from "./chunk-32R4IEGS.js";
15
15
  import {
16
16
  PiTransport,
17
17
  makePiOperatorResources,
@@ -19,7 +19,7 @@ import {
19
19
  makePiTransportLayer,
20
20
  requirePiModelCatalog,
21
21
  require_undici
22
- } from "./chunk-GCJD6IMI.js";
22
+ } from "./chunk-NBNWSCBX.js";
23
23
  import {
24
24
  AgentRole,
25
25
  AgentRoleSchema,
@@ -66,7 +66,9 @@ import {
66
66
  campProjectPaths,
67
67
  campRecoveryDiagnosticFromCause,
68
68
  campRunPaths,
69
+ currentDirectToolSandbox,
69
70
  directToolName,
71
+ directToolSandboxCommand,
70
72
  findCampRecoveryHealthComponent,
71
73
  fromDuplex,
72
74
  fromWebSocket,
@@ -75,6 +77,7 @@ import {
75
77
  loadCampManifest,
76
78
  loadReferencedMcpCredentials,
77
79
  loadWorkerConfig,
80
+ parseDirectToolSandboxStatus,
78
81
  prepareCampRunStorage,
79
82
  prepareCampStorage,
80
83
  projectScopeFromCwd,
@@ -82,6 +85,7 @@ import {
82
85
  resolveCampMcpUnion,
83
86
  resolveDefaultPreset,
84
87
  resolveDirectTools,
88
+ resolveExecutablePath,
85
89
  resolveHirePreset,
86
90
  resolveMcpSecretEnv,
87
91
  resolveMcpServers,
@@ -96,7 +100,7 @@ import {
96
100
  runningCampManifest,
97
101
  saveCampHealth,
98
102
  saveCampManifest
99
- } from "./chunk-T6NUWP6F.js";
103
+ } from "./chunk-LIJU6Y2U.js";
100
104
  import {
101
105
  CampAskRouter,
102
106
  CampCancellationCode,
@@ -11194,7 +11198,7 @@ var require_cross_spawn = __commonJS({
11194
11198
  var cp = __require("child_process");
11195
11199
  var parse3 = require_parse();
11196
11200
  var enoent = require_enoent();
11197
- function spawn2(command, args, options) {
11201
+ function spawn3(command, args, options) {
11198
11202
  const parsed = parse3(command, args, options);
11199
11203
  const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
11200
11204
  enoent.hookChildProcess(spawned, parsed);
@@ -11206,8 +11210,8 @@ var require_cross_spawn = __commonJS({
11206
11210
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
11207
11211
  return result;
11208
11212
  }
11209
- module.exports = spawn2;
11210
- module.exports.spawn = spawn2;
11213
+ module.exports = spawn3;
11214
+ module.exports.spawn = spawn3;
11211
11215
  module.exports.sync = spawnSync;
11212
11216
  module.exports._parse = parse3;
11213
11217
  module.exports._enoent = enoent;
@@ -22412,7 +22416,7 @@ var Client = class extends Protocol {
22412
22416
  };
22413
22417
 
22414
22418
  // packages/mcp-backend/src/connections/clientIdentity.ts
22415
- var backendVersion = "0.1.3";
22419
+ var backendVersion = "0.1.5";
22416
22420
 
22417
22421
  // packages/mcp-backend/src/connections/mcpFailure.ts
22418
22422
  var McpFailure = class extends Data_exports.TaggedError("McpFailure") {
@@ -26372,7 +26376,7 @@ var makeCampRpcServerLayer = (socketPath) => {
26372
26376
  };
26373
26377
 
26374
26378
  // packages/host/src/tools/directTools.ts
26375
- import { execFile } from "node:child_process";
26379
+ import { spawn as spawn2 } from "node:child_process";
26376
26380
 
26377
26381
  // packages/host/src/tools/directToolPolicy.ts
26378
26382
  var flagName = (argument) => {
@@ -26419,39 +26423,78 @@ var truncate = (value, limit) => {
26419
26423
  return encoded.byteLength <= limit ? { text: value, truncated: false } : { text: encoded.subarray(0, limit).toString("utf8"), truncated: true };
26420
26424
  };
26421
26425
  var binaryCandidates = (bin) => bin === "fd" ? ["fd", "fdfind"] : [bin];
26422
- var runBinary = (bin, args, options, signal) => new Promise((resolve, reject) => {
26423
- const child = execFile(
26424
- bin,
26425
- [...args],
26426
- {
26426
+ var collect = (stream, onOverflow) => {
26427
+ const chunks = [];
26428
+ let size = 0;
26429
+ let overflowed = false;
26430
+ stream?.on("data", (chunk) => {
26431
+ if (overflowed) return;
26432
+ size += chunk.byteLength;
26433
+ if (size > MAX_BUFFER_BYTES) {
26434
+ overflowed = true;
26435
+ onOverflow();
26436
+ return;
26437
+ }
26438
+ chunks.push(chunk);
26439
+ });
26440
+ return { text: () => Buffer.concat(chunks).toString("utf8") };
26441
+ };
26442
+ var runBinary = (executablePath, args, options, environment, signal) => {
26443
+ const sandbox = options.sandbox ?? currentDirectToolSandbox();
26444
+ const launch = directToolSandboxCommand(sandbox, executablePath, args, { cwd: options.cwd });
26445
+ const sandboxed = launch.statusFd !== void 0;
26446
+ return new Promise((resolve) => {
26447
+ const child = spawn2(launch.command, [...launch.args], {
26427
26448
  cwd: options.cwd,
26428
- env: directToolEnvironment(options.environment ?? process.env),
26429
- timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
26430
- maxBuffer: MAX_BUFFER_BYTES,
26449
+ env: environment,
26450
+ stdio: sandboxed ? ["ignore", "pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
26431
26451
  ...signal === void 0 ? {} : { signal }
26432
- },
26433
- (error2, stdout, stderr) => {
26434
- const failure = error2;
26435
- if (failure !== null && failure.code === "ENOENT") {
26436
- reject(failure);
26437
- return;
26438
- }
26452
+ });
26453
+ let timedOut = false;
26454
+ let launchError;
26455
+ const stop = () => {
26456
+ child.kill("SIGTERM");
26457
+ };
26458
+ const stdout = collect(child.stdout, stop);
26459
+ const stderr = collect(child.stderr, stop);
26460
+ const status = collect(
26461
+ sandboxed ? child.stdio[launch.statusFd ?? 3] ?? null : null,
26462
+ stop
26463
+ );
26464
+ const timer = setTimeout(() => {
26465
+ timedOut = true;
26466
+ stop();
26467
+ }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
26468
+ child.on("error", (cause) => {
26469
+ if (signal?.aborted === true || cause.name === "AbortError") return;
26470
+ launchError = `${cause.code ?? cause.name}: ${cause.message}`;
26471
+ });
26472
+ child.on("close", (code, terminatedBy) => {
26473
+ clearTimeout(timer);
26474
+ const sandboxStatus = sandboxed ? parseDirectToolSandboxStatus(status.text()) : void 0;
26439
26475
  resolve({
26440
- stdout,
26441
- stderr,
26442
- exitCode: typeof failure?.code === "number" ? failure.code : failure === null ? 0 : 1,
26443
- signal: child.signalCode ?? void 0,
26444
- timedOut: failure?.killed === true
26476
+ stdout: stdout.text(),
26477
+ stderr: stderr.text(),
26478
+ exitCode: sandboxStatus?.exitCode ?? code ?? void 0,
26479
+ signal: terminatedBy ?? void 0,
26480
+ timedOut,
26481
+ /*
26482
+ * A sandbox that never reached the command is only a refusal when the
26483
+ * command was not stopped from the outside: a killed sandbox reports no
26484
+ * child either, and that is the timeout, not the boundary.
26485
+ */
26486
+ ...sandboxStatus !== void 0 && !sandboxStatus.started && !timedOut && launchError === void 0 ? { sandboxFailed: true } : {},
26487
+ ...launchError === void 0 ? {} : { launchError }
26445
26488
  });
26446
- }
26447
- );
26448
- });
26489
+ });
26490
+ });
26491
+ };
26449
26492
  var runWithFallback = async (policy, args, options, signal) => {
26493
+ const environment = directToolEnvironment(options.environment ?? process.env);
26450
26494
  for (const candidate of binaryCandidates(policy.bin)) {
26451
- try {
26452
- return await runBinary(candidate, args, options, signal);
26453
- } catch {
26454
- }
26495
+ const executablePath = resolveExecutablePath(candidate, environment);
26496
+ if (executablePath === void 0) continue;
26497
+ return await runBinary(executablePath, args, options, environment, signal);
26455
26498
  }
26456
26499
  return void 0;
26457
26500
  };
@@ -26523,7 +26566,19 @@ var makeDirectTool = (declared, options) => ({
26523
26566
  return refusal(detail);
26524
26567
  }
26525
26568
  const run = await runWithFallback(policy, args, options, signal);
26526
- return run === void 0 ? refusal(`\`${policy.bin}\` is not available on this machine.`) : resultOf(policy, run, options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES);
26569
+ if (run === void 0) {
26570
+ return refusal(`\`${policy.bin}\` is not available on this machine.`);
26571
+ }
26572
+ if (run.sandboxFailed === true) {
26573
+ return refusal(
26574
+ `\`${policy.bin}\` was not run: the OS sandbox refused to start it.
26575
+ ${run.stderr.trim()}`
26576
+ );
26577
+ }
26578
+ if (run.launchError !== void 0) {
26579
+ return refusal(`\`${policy.bin}\` could not be started: ${run.launchError}`);
26580
+ }
26581
+ return resultOf(policy, run, options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES);
26527
26582
  }
26528
26583
  });
26529
26584
  var makeDirectTools = (declared, options) => declared.map(
package/dist/priiisk.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  fromReadable,
6
6
  layer,
7
7
  runMain
8
- } from "./chunk-C7GQZ3PV.js";
8
+ } from "./chunk-Q7YCBJEO.js";
9
9
  import {
10
10
  CliCampLifecycleError,
11
11
  CliEquipmentConflict,
@@ -23,7 +23,7 @@ import {
23
23
  resolveCampSurface,
24
24
  withCampRpc,
25
25
  withCampRpcSession
26
- } from "./chunk-VHGA3U67.js";
26
+ } from "./chunk-F5HRZ52I.js";
27
27
  import {
28
28
  CampCommandFailure,
29
29
  CampHostReadiness,
@@ -58,7 +58,7 @@ import {
58
58
  resolveProjectScopeSync,
59
59
  saveCampManifest,
60
60
  startingCampManifest
61
- } from "./chunk-T6NUWP6F.js";
61
+ } from "./chunk-LIJU6Y2U.js";
62
62
  import {
63
63
  Cause_exports,
64
64
  CommitPrototype,
@@ -19903,7 +19903,7 @@ var campResumeListCommand = Command_exports.make(
19903
19903
  )
19904
19904
  ).pipe(Command_exports.withDescription("Lists saved runs of this project that resume can restore."));
19905
19905
  var runIdArgument = Args_exports.text({ name: "runId" }).pipe(Args_exports.optional);
19906
- var selectCampRunOnDemand = (items) => Effect_exports.promise(() => import("./chunk-PIZUXHCN.js")).pipe(
19906
+ var selectCampRunOnDemand = (items) => Effect_exports.promise(() => import("./chunk-TOVFB7H6.js")).pipe(
19907
19907
  Effect_exports.flatMap((ui) => ui.selectCampRun({ items }))
19908
19908
  );
19909
19909
  var selectAndResume = (json) => {
@@ -19976,7 +19976,7 @@ var rootCommand = Command_exports.make(
19976
19976
  ).pipe(Command_exports.withDescription("Runs and observes a camp of collaborating workers."));
19977
19977
 
19978
19978
  // packages/cli/src/commands/doctorCommand.ts
19979
- var runDoctorOnDemand = Effect_exports.promise(() => import("./chunk-4IQLMJ25.js")).pipe(
19979
+ var runDoctorOnDemand = Effect_exports.promise(() => import("./chunk-2RSY5TXT.js")).pipe(
19980
19980
  Effect_exports.flatMap((doctor) => doctor.runDoctor)
19981
19981
  );
19982
19982
  var statusLabel = {
@@ -20672,7 +20672,7 @@ var command = rootCommand.pipe(
20672
20672
  );
20673
20673
  var cli = Command_exports.run(command, {
20674
20674
  name: "priiisk camp control",
20675
- version: "0.1.3"
20675
+ version: "0.1.5"
20676
20676
  });
20677
20677
  var runCliCommand = (argumentsList) => cli(argumentsList).pipe(
20678
20678
  Effect_exports.matchEffect({
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "priiisk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for running and observing a camp of collaborating agents",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "priiisk": "./dist/priiisk.js"
8
8
  },
9
9
  "files": [
10
- "dist/chunk-4IQLMJ25.js",
11
- "dist/chunk-C7GQZ3PV.js",
12
- "dist/chunk-GCJD6IMI.js",
13
- "dist/chunk-GCJD6IMI.js.LEGAL.txt",
10
+ "dist/chunk-2RSY5TXT.js",
11
+ "dist/chunk-32R4IEGS.js",
12
+ "dist/chunk-F5HRZ52I.js",
14
13
  "dist/chunk-KFSFN6L5.js",
15
- "dist/chunk-PIZUXHCN.js",
16
- "dist/chunk-T6NUWP6F.js",
17
- "dist/chunk-T6NUWP6F.js.LEGAL.txt",
18
- "dist/chunk-VHGA3U67.js",
19
- "dist/chunk-Z64QXV3O.js",
14
+ "dist/chunk-LIJU6Y2U.js",
15
+ "dist/chunk-LIJU6Y2U.js.LEGAL.txt",
16
+ "dist/chunk-NBNWSCBX.js",
17
+ "dist/chunk-NBNWSCBX.js.LEGAL.txt",
18
+ "dist/chunk-Q7YCBJEO.js",
19
+ "dist/chunk-TOVFB7H6.js",
20
20
  "dist/priiisk-host.js",
21
21
  "dist/priiisk.js",
22
22
  "profiles",