pi-radar 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ var radar_children_exports = {};
9
9
  __export(radar_children_exports, {
10
10
  createRadarChildren: () => createRadarChildren
11
11
  });
12
+ import { randomUUID as randomUUID2 } from "node:crypto";
12
13
 
13
14
  // extension/radar-inspection.ts
14
15
  var radar_inspection_exports = {};
@@ -60,10 +61,12 @@ function utf8Tail(value, maxBytes) {
60
61
  // extension/radar-launch.ts
61
62
  var radar_launch_exports = {};
62
63
  __export(radar_launch_exports, {
64
+ launchFailure: () => launchFailure,
63
65
  launchRadarWorker: () => launchRadarWorker,
64
66
  observeRadarChild: () => observeRadarChild,
65
67
  readProcessGone: () => readProcessGone,
66
- relaunchRefusalForChild: () => relaunchRefusalForChild
68
+ relaunchRefusalForChild: () => relaunchRefusalForChild,
69
+ waitForChild: () => waitForChild
67
70
  });
68
71
  import { setTimeout as delay } from "node:timers/promises";
69
72
 
@@ -104,7 +107,7 @@ import { lstatSync } from "node:fs";
104
107
  import { createConnection } from "node:net";
105
108
  import { dirname, join } from "node:path";
106
109
  var RADAR_PROTOCOL_VERSION = 2;
107
- var RADAR_MINIMUM_REVISION = 7;
110
+ var RADAR_MINIMUM_REVISION = 8;
108
111
  var RADAR_REGISTRY_CONTRACT = "registry.v1";
109
112
  var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
110
113
  var RADAR_SPAWN_CONTRACT = "spawn.v1";
@@ -418,6 +421,9 @@ function spawnEdge(value) {
418
421
  const agentId = stringAt(bound, "registration_id");
419
422
  const subjectId = stringAt(bound, "subject_id");
420
423
  const exit = exitAt(recordAt(value, "exit_observation"));
424
+ const withdrawnRecord = recordAt(value, "withdrawn");
425
+ const withdrawnAt = stringAt(withdrawnRecord, "at");
426
+ const withdrawnReason = stringAt(withdrawnRecord, "reason");
421
427
  return {
422
428
  ok: true,
423
429
  value: {
@@ -434,7 +440,9 @@ function spawnEdge(value) {
434
440
  ...isUuid(subjectId) ? { subject_id: subjectId } : {}
435
441
  }
436
442
  } : {},
437
- ...exit !== void 0 ? { exit_observation: exit } : {}
443
+ ...exit !== void 0 ? { exit_observation: exit } : {},
444
+ ...stringAt(value, "granted_at") !== void 0 ? { granted_at: stringAt(value, "granted_at") } : {},
445
+ ...withdrawnAt !== void 0 && withdrawnReason !== void 0 ? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } } : {}
438
446
  }
439
447
  };
440
448
  }
@@ -1024,7 +1032,11 @@ function createRadarClient(options = {}) {
1024
1032
  request.request_id
1025
1033
  );
1026
1034
  if (answer.ok === false) return answer;
1027
- return focusOutcome(recordAt(answer.value, "request"), request.request_id);
1035
+ const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
1036
+ if (settled.ok === false) return settled;
1037
+ const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
1038
+ if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
1039
+ return { ok: true, value: { ...settled.value, launch_request_id: launch } };
1028
1040
  });
1029
1041
  },
1030
1042
  async spawnList(request) {
@@ -1394,123 +1406,6 @@ function boundedRadarSnapshot(snapshot) {
1394
1406
  };
1395
1407
  }
1396
1408
 
1397
- // extension/radar-location.ts
1398
- var radar_location_exports = {};
1399
- __export(radar_location_exports, {
1400
- radarLocalPane: () => radarLocalPane,
1401
- radarPaneHandle: () => radarPaneHandle,
1402
- resolveRadarProcessLocation: () => resolveRadarProcessLocation,
1403
- tmuxServerName: () => tmuxServerName
1404
- });
1405
- import { basename } from "node:path";
1406
- function radarLocalPane(env) {
1407
- const pane = env.TMUX_PANE?.trim();
1408
- return pane && /^%\d+$/.test(pane) ? pane : void 0;
1409
- }
1410
- function radarPaneHandle(qualified) {
1411
- return qualified.slice(qualified.lastIndexOf(":") + 1);
1412
- }
1413
- function tmuxServerName(env) {
1414
- const socket = env.TMUX?.trim().split(",")[0];
1415
- return socket ? basename(socket) || void 0 : void 0;
1416
- }
1417
- function qualifiedOwnPane(env, backend, instance) {
1418
- if (backend !== "tmux")
1419
- return {
1420
- absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
1421
- };
1422
- if (instance === void 0 || instance === "")
1423
- return {
1424
- pending: "the daemon did not name the tmux server it addresses"
1425
- };
1426
- const server = tmuxServerName(env);
1427
- if (server === void 0)
1428
- return {
1429
- absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
1430
- };
1431
- if (server !== instance)
1432
- return {
1433
- absent: `this process is attached to tmux server ${server}, not to ${instance}`
1434
- };
1435
- const pane = radarLocalPane(env);
1436
- if (pane === void 0)
1437
- return {
1438
- absent: "this process's own environment names no tmux pane of that server"
1439
- };
1440
- return { pane: `tmux:${instance}:${pane}` };
1441
- }
1442
- function observedContainment(inventory, paneId) {
1443
- const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
1444
- if (rows.length === 0) return void 0;
1445
- if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
1446
- const row = rows[0];
1447
- const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
1448
- if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
1449
- return {
1450
- ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
1451
- };
1452
- return { pane: row };
1453
- }
1454
- async function resolveRadarProcessLocation(client, env = process.env) {
1455
- let ready;
1456
- try {
1457
- ready = await client.ping();
1458
- } catch {
1459
- return { state: "pending", reason: "the daemon did not answer this process" };
1460
- }
1461
- if (ready.ok === false)
1462
- return {
1463
- state: "pending",
1464
- reason: `the daemon did not answer this process (${ready.code})`
1465
- };
1466
- const backend = ready.value.backend;
1467
- if (backend === void 0 || backend === "none")
1468
- return { state: "absent", reason: "the daemon serves no physical backend" };
1469
- const instance = ready.value.instance ?? void 0;
1470
- const own = qualifiedOwnPane(env, backend, instance);
1471
- if ("absent" in own) return { state: "absent", reason: own.absent };
1472
- if ("pending" in own) return { state: "pending", reason: own.pending };
1473
- const paneId = own.pane;
1474
- if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
1475
- return {
1476
- state: "absent",
1477
- reason: "this process is in the pane its launch was created beside, not in a pane of its own"
1478
- };
1479
- let inventory;
1480
- try {
1481
- inventory = await client.observe();
1482
- } catch {
1483
- return {
1484
- state: "pending",
1485
- reason: "the daemon's topology could not be read for this process's pane"
1486
- };
1487
- }
1488
- if (inventory.ok === false)
1489
- return {
1490
- state: "pending",
1491
- reason: `the daemon's topology could not be read (${inventory.code})`
1492
- };
1493
- const containment = observedContainment(inventory.value, paneId);
1494
- if (containment === void 0)
1495
- return {
1496
- state: "pending",
1497
- reason: `the daemon's topology does not report pane ${paneId} yet`
1498
- };
1499
- if ("ambiguous" in containment)
1500
- return { state: "pending", reason: containment.ambiguous };
1501
- const row = containment.pane;
1502
- return {
1503
- state: "resolved",
1504
- location: {
1505
- backend,
1506
- ...instance !== void 0 ? { instance } : {},
1507
- workspace: row.workspace_id,
1508
- tab: row.tab_id,
1509
- pane: paneId
1510
- }
1511
- };
1512
- }
1513
-
1514
1409
  // extension/radar-launch.ts
1515
1410
  function unknownChild(reason) {
1516
1411
  return { kind: "unknown", diagnostic: trajectory(reason) };
@@ -1602,7 +1497,7 @@ async function relaunchRefusalForChild(client, agentId) {
1602
1497
  return `the child's registered process is ${verification === "verified" ? "still running" : "not proved gone"}`;
1603
1498
  }
1604
1499
  function refusedLaunch(message) {
1605
- return { kind: "refused", message };
1500
+ return { kind: "failed", message };
1606
1501
  }
1607
1502
  function unresolvedLaunch(requestId, message) {
1608
1503
  return {
@@ -1611,14 +1506,6 @@ function unresolvedLaunch(requestId, message) {
1611
1506
  message: `spawn ${requestId} is unresolved (${message})`
1612
1507
  };
1613
1508
  }
1614
- function edgeStanding(edge) {
1615
- if (edge.created === "refused" || edge.launched === "refused")
1616
- return "refused";
1617
- return edge.created === "completed" && edge.launched === "completed" ? "launched" : "unknown";
1618
- }
1619
- function edgeMessage(edge) {
1620
- return `the daemon's spawn record is created ${edge.created} and launched ${edge.launched}`;
1621
- }
1622
1509
  var PRE_EFFECT_CODES = [
1623
1510
  "absent",
1624
1511
  "unsupported",
@@ -1631,14 +1518,12 @@ var PRE_EFFECT_CODES = [
1631
1518
  "busy",
1632
1519
  "backend_unavailable"
1633
1520
  ];
1634
- function spawnFailure(result, requestId) {
1521
+ function launchFailure(result, requestId) {
1635
1522
  return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
1636
1523
  }
1637
1524
  async function launchRadarWorker(options) {
1638
1525
  const { client, launch, signal } = options;
1639
1526
  const pollMs = options.pollMs ?? 250;
1640
- const deadline = Date.now() + options.timeoutMs;
1641
- const remaining = () => Math.max(0, deadline - Date.now());
1642
1527
  const parent = await options.publication.subject();
1643
1528
  if (parent === void 0 || !isUuid(parent.registration_id))
1644
1529
  return refusedLaunch(
@@ -1669,55 +1554,22 @@ async function launchRadarWorker(options) {
1669
1554
  ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
1670
1555
  });
1671
1556
  if (spawned.ok === false) {
1672
- const refusal = spawnFailure(spawned, launch.request_id);
1673
- if (refusal.kind === "refused") return refusal;
1674
- }
1675
- if (spawned.ok === true) {
1676
- const standing = edgeStanding(spawned.value);
1677
- if (standing === "refused")
1678
- return refusedLaunch(edgeMessage(spawned.value));
1679
- if (standing === "unknown")
1680
- return unresolvedLaunch(launch.request_id, edgeMessage(spawned.value));
1557
+ const refusal = launchFailure(spawned, launch.request_id);
1558
+ if (refusal.kind === "failed") return refusal;
1681
1559
  }
1682
1560
  const bound = await waitForChild(client, launch.request_id, {
1683
- remaining,
1684
1561
  pollMs,
1685
1562
  ...signal !== void 0 ? { signal } : {}
1686
1563
  });
1687
1564
  if (bound.kind !== "bound") return bound;
1688
- const agentId = bound.registration_id;
1689
- try {
1690
- const state = await options.readers.waitForState(
1691
- launch.mailbox,
1692
- (candidate) => options.readers.startupAckMatches(candidate, {
1693
- generation: launch.generation,
1694
- runId: launch.identity.runId,
1695
- ownerSessionId: launch.identity.ownerSessionId,
1696
- workspaceId: launch.identity.workspaceId,
1697
- agentLabel: launch.identity.agentLabel,
1698
- // The child writes the raw TMUX_PANE of the pane it runs in. Requiring
1699
- // the pane the daemon created is what refuses a child that inherited
1700
- // another process's coordinates.
1701
- paneId: radarPaneHandle(bound.pane_id)
1702
- }),
1703
- { timeoutMs: remaining(), ...signal !== void 0 ? { signal } : {} }
1704
- );
1705
- return {
1706
- kind: "ready",
1707
- value: {
1708
- request_id: launch.request_id,
1709
- subject_id: bound.subject_id,
1710
- pane_id: bound.pane_id,
1711
- state
1712
- }
1713
- };
1714
- } catch (error) {
1715
- const claimed = options.readers.readState(launch.mailbox)?.paneId;
1716
- return unresolvedLaunch(
1717
- launch.request_id,
1718
- claimed !== void 0 && claimed !== radarPaneHandle(bound.pane_id) ? `the bound child ${agentId} claims the pane ${claimed}, not the pane ${bound.pane_id} this launch created for it` : `the bound child ${agentId} has not acknowledged this launch's startup (${String(error)})`
1719
- );
1720
- }
1565
+ return {
1566
+ kind: "ready",
1567
+ value: {
1568
+ request_id: launch.request_id,
1569
+ subject_id: bound.subject_id,
1570
+ pane_id: bound.pane_id
1571
+ }
1572
+ };
1721
1573
  }
1722
1574
  async function waitForChild(client, requestId, options) {
1723
1575
  let last = "the daemon has not answered";
@@ -1726,37 +1578,21 @@ async function waitForChild(client, requestId, options) {
1726
1578
  return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
1727
1579
  const answer = await client.spawnGet(requestId);
1728
1580
  if (answer.ok === true) {
1729
- const { edge, state, freshness } = answer.value;
1730
- const standing = edgeStanding(edge);
1731
- if (standing === "refused")
1732
- return refusedLaunch(`${edgeMessage(edge)}`);
1733
- if (standing === "unknown")
1734
- return unresolvedLaunch(requestId, edgeMessage(edge));
1581
+ const { edge } = answer.value;
1582
+ if (edge.withdrawn !== void 0)
1583
+ return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
1584
+ if (edge.granted_at === void 0) {
1585
+ await delay(options.pollMs);
1586
+ continue;
1587
+ }
1735
1588
  const pane = edge.location?.pane;
1736
1589
  if (pane === void 0 || pane === "")
1737
1590
  return unresolvedLaunch(
1738
1591
  requestId,
1739
1592
  "the daemon's launch recorded no pane to verify the child against"
1740
1593
  );
1741
- if (freshness !== "fresh")
1742
- return unresolvedLaunch(
1743
- requestId,
1744
- state === "unresolved" ? `the daemon's inventory does not report the pane ${pane} its own record names for this child` : `the daemon cannot confirm the pane ${pane} it recorded for this child`
1745
- );
1746
- if (edge.exit_observation?.state === "exited")
1747
- return unresolvedLaunch(
1748
- requestId,
1749
- "the daemon observed the launched child's process exit"
1750
- );
1751
1594
  const agentId = edge.bound?.registration_id;
1752
1595
  if (agentId !== void 0 && isUuid(agentId)) {
1753
- const source = edge.bound?.source;
1754
- const incarnation = edge.bound?.incarnation;
1755
- if (source === void 0 || source === "" || incarnation === void 0 || incarnation === "")
1756
- return unresolvedLaunch(
1757
- requestId,
1758
- `the daemon bound the child ${agentId} without recording the process it registered as`
1759
- );
1760
1596
  const subjectId = edge.bound?.subject_id;
1761
1597
  if (subjectId === void 0 || !isUuid(subjectId))
1762
1598
  return unresolvedLaunch(
@@ -1766,21 +1602,14 @@ async function waitForChild(client, requestId, options) {
1766
1602
  return {
1767
1603
  kind: "bound",
1768
1604
  subject_id: subjectId,
1769
- registration_id: agentId,
1770
- pane_id: pane,
1771
- source,
1772
- incarnation
1605
+ pane_id: pane
1773
1606
  };
1774
1607
  }
1775
1608
  last = "no child has registered with this launch's token yet";
1776
1609
  } else {
1777
- last = answer.message;
1610
+ return unresolvedLaunch(requestId, answer.message);
1778
1611
  }
1779
- if (options.remaining() <= 0)
1780
- return unresolvedLaunch(requestId, last);
1781
- await delay(options.pollMs, void 0, {
1782
- ...options.signal !== void 0 ? { signal: options.signal } : {}
1783
- });
1612
+ await delay(options.pollMs);
1784
1613
  }
1785
1614
  }
1786
1615
 
@@ -1797,7 +1626,7 @@ function createRadarChildren(client, publication) {
1797
1626
  if (read.ok === false) return read;
1798
1627
  for (const edge of read.value.spawns) {
1799
1628
  const bound = edge.bound;
1800
- if (!bound?.subject_id || !bound.registration_id || edge.launched !== "completed") continue;
1629
+ if (!bound?.subject_id || !bound.registration_id || edge.granted_at == null || edge.withdrawn != null) continue;
1801
1630
  const at = Date.parse(bound.bound_at);
1802
1631
  if (Number.isNaN(at)) continue;
1803
1632
  const current = newest.get(bound.subject_id);
@@ -1843,6 +1672,34 @@ function createRadarChildren(client, publication) {
1843
1672
  follow(all.value);
1844
1673
  return { ok: true, value: [...all.value.values()].map((entry) => entry.child) };
1845
1674
  },
1675
+ async restart(subject, program, options) {
1676
+ const parent = await publication.subject();
1677
+ if (parent?.subject_id === void 0)
1678
+ return { kind: "failed", message: "this process has no Radar registration to restart a child under" };
1679
+ const request_id = randomUUID2();
1680
+ const requested = await client.restart({
1681
+ request_id,
1682
+ parent: parent.registration_id,
1683
+ subject_id: subject,
1684
+ ...program
1685
+ });
1686
+ if (requested.ok === false)
1687
+ return launchFailure(requested, request_id);
1688
+ const bound = await waitForChild(client, requested.value.launch_request_id, {
1689
+ pollMs: options?.pollMs ?? 250,
1690
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
1691
+ });
1692
+ if (bound.kind !== "bound") return bound;
1693
+ await resolveAndFollow(subject);
1694
+ return {
1695
+ kind: "ready",
1696
+ value: {
1697
+ request_id: requested.value.launch_request_id,
1698
+ subject_id: bound.subject_id,
1699
+ pane_id: bound.pane_id
1700
+ }
1701
+ };
1702
+ },
1846
1703
  assignment(subject, owner) {
1847
1704
  const existing = held.get(subject);
1848
1705
  if (existing !== void 0) return existing.writer;
@@ -1964,7 +1821,7 @@ __export(radar_publication_exports, {
1964
1821
  radarProcessSlot: () => radarProcessSlot,
1965
1822
  readProcessBirth: () => readProcessBirth
1966
1823
  });
1967
- import { createHash, randomUUID as randomUUID2 } from "node:crypto";
1824
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
1968
1825
  import {
1969
1826
  closeSync,
1970
1827
  constants,
@@ -2094,7 +1951,7 @@ function createPublicationStore(root) {
2094
1951
  ensureDirectory(directory);
2095
1952
  const temporary = join2(
2096
1953
  directory,
2097
- `.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID2()}.tmp`
1954
+ `.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID3()}.tmp`
2098
1955
  );
2099
1956
  const bytes = Buffer.from(JSON.stringify(value), "utf8");
2100
1957
  if (bytes.length > MAX_RECORD_BYTES)
@@ -2150,7 +2007,7 @@ function radarProcessSlot() {
2150
2007
  return host[PROCESS_SLOT];
2151
2008
  }
2152
2009
  function radarProcessIncarnation(slot = radarProcessSlot()) {
2153
- return slot.incarnation ??= randomUUID2();
2010
+ return slot.incarnation ??= randomUUID3();
2154
2011
  }
2155
2012
  function radarBindingKey(run, owner, label) {
2156
2013
  return createHash("sha256").update(`${run}\0${owner}\0${label}`).digest("hex").slice(0, 32);
@@ -2618,7 +2475,7 @@ function createPresenceReporter(input) {
2618
2475
  releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
2619
2476
  }
2620
2477
  standing = void 0;
2621
- state = { version: 2, session, claim_id: randomUUID2(), releases };
2478
+ state = { version: 2, session, claim_id: randomUUID3(), releases };
2622
2479
  save();
2623
2480
  stopped = false;
2624
2481
  void ensure();
@@ -2655,7 +2512,7 @@ var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
2655
2512
  function storeIdentity(store) {
2656
2513
  const known = STORE_IDENTITY.get(store);
2657
2514
  if (known !== void 0) return known;
2658
- const created = randomUUID2();
2515
+ const created = randomUUID3();
2659
2516
  STORE_IDENTITY.set(store, created);
2660
2517
  return created;
2661
2518
  }
@@ -3182,6 +3039,123 @@ function createRadarExecutionAdapter(pi, options) {
3182
3039
  };
3183
3040
  }
3184
3041
 
3042
+ // extension/radar-location.ts
3043
+ var radar_location_exports = {};
3044
+ __export(radar_location_exports, {
3045
+ radarLocalPane: () => radarLocalPane,
3046
+ radarPaneHandle: () => radarPaneHandle,
3047
+ resolveRadarProcessLocation: () => resolveRadarProcessLocation,
3048
+ tmuxServerName: () => tmuxServerName
3049
+ });
3050
+ import { basename } from "node:path";
3051
+ function radarLocalPane(env) {
3052
+ const pane = env.TMUX_PANE?.trim();
3053
+ return pane && /^%\d+$/.test(pane) ? pane : void 0;
3054
+ }
3055
+ function radarPaneHandle(qualified) {
3056
+ return qualified.slice(qualified.lastIndexOf(":") + 1);
3057
+ }
3058
+ function tmuxServerName(env) {
3059
+ const socket = env.TMUX?.trim().split(",")[0];
3060
+ return socket ? basename(socket) || void 0 : void 0;
3061
+ }
3062
+ function qualifiedOwnPane(env, backend, instance) {
3063
+ if (backend !== "tmux")
3064
+ return {
3065
+ absent: `the daemon serves the ${backend} backend, and Herdsman drives only tmux`
3066
+ };
3067
+ if (instance === void 0 || instance === "")
3068
+ return {
3069
+ pending: "the daemon did not name the tmux server it addresses"
3070
+ };
3071
+ const server = tmuxServerName(env);
3072
+ if (server === void 0)
3073
+ return {
3074
+ absent: "this process's own tmux client names no server, so it is not in the pane it would publish"
3075
+ };
3076
+ if (server !== instance)
3077
+ return {
3078
+ absent: `this process is attached to tmux server ${server}, not to ${instance}`
3079
+ };
3080
+ const pane = radarLocalPane(env);
3081
+ if (pane === void 0)
3082
+ return {
3083
+ absent: "this process's own environment names no tmux pane of that server"
3084
+ };
3085
+ return { pane: `tmux:${instance}:${pane}` };
3086
+ }
3087
+ function observedContainment(inventory, paneId) {
3088
+ const rows = inventory.panes.filter((pane) => pane.pane_id === paneId);
3089
+ if (rows.length === 0) return void 0;
3090
+ if (rows.length > 1) return { ambiguous: `topology reports pane ${paneId} twice` };
3091
+ const row = rows[0];
3092
+ const tab = inventory.tabs.find((candidate) => candidate.tab_id === row.tab_id);
3093
+ if (tab !== void 0 && tab.workspace_id !== row.workspace_id)
3094
+ return {
3095
+ ambiguous: `pane ${paneId} is reported in ${row.workspace_id}, but its tab belongs to ${tab.workspace_id}`
3096
+ };
3097
+ return { pane: row };
3098
+ }
3099
+ async function resolveRadarProcessLocation(client, env = process.env) {
3100
+ let ready;
3101
+ try {
3102
+ ready = await client.ping();
3103
+ } catch {
3104
+ return { state: "pending", reason: "the daemon did not answer this process" };
3105
+ }
3106
+ if (ready.ok === false)
3107
+ return {
3108
+ state: "pending",
3109
+ reason: `the daemon did not answer this process (${ready.code})`
3110
+ };
3111
+ const backend = ready.value.backend;
3112
+ if (backend === void 0 || backend === "none")
3113
+ return { state: "absent", reason: "the daemon serves no physical backend" };
3114
+ const instance = ready.value.instance ?? void 0;
3115
+ const own = qualifiedOwnPane(env, backend, instance);
3116
+ if ("absent" in own) return { state: "absent", reason: own.absent };
3117
+ if ("pending" in own) return { state: "pending", reason: own.pending };
3118
+ const paneId = own.pane;
3119
+ if (paneId === env.PI_HERDSMAN_PARENT_PANE_ID?.trim())
3120
+ return {
3121
+ state: "absent",
3122
+ reason: "this process is in the pane its launch was created beside, not in a pane of its own"
3123
+ };
3124
+ let inventory;
3125
+ try {
3126
+ inventory = await client.observe();
3127
+ } catch {
3128
+ return {
3129
+ state: "pending",
3130
+ reason: "the daemon's topology could not be read for this process's pane"
3131
+ };
3132
+ }
3133
+ if (inventory.ok === false)
3134
+ return {
3135
+ state: "pending",
3136
+ reason: `the daemon's topology could not be read (${inventory.code})`
3137
+ };
3138
+ const containment = observedContainment(inventory.value, paneId);
3139
+ if (containment === void 0)
3140
+ return {
3141
+ state: "pending",
3142
+ reason: `the daemon's topology does not report pane ${paneId} yet`
3143
+ };
3144
+ if ("ambiguous" in containment)
3145
+ return { state: "pending", reason: containment.ambiguous };
3146
+ const row = containment.pane;
3147
+ return {
3148
+ state: "resolved",
3149
+ location: {
3150
+ backend,
3151
+ ...instance !== void 0 ? { instance } : {},
3152
+ workspace: row.workspace_id,
3153
+ tab: row.tab_id,
3154
+ pane: paneId
3155
+ }
3156
+ };
3157
+ }
3158
+
3185
3159
  // extension/radar-runtime.ts
3186
3160
  var radar_runtime_exports = {};
3187
3161
  __export(radar_runtime_exports, {
@@ -3192,7 +3166,7 @@ __export(radar_runtime_exports, {
3192
3166
  requestRadarRuntime: () => requestRadarRuntime
3193
3167
  });
3194
3168
  var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
3195
- var RADAR_RUNTIME_VERSION = 3;
3169
+ var RADAR_RUNTIME_VERSION = 4;
3196
3170
  function lazyRadarRuntime(start) {
3197
3171
  let pending;
3198
3172
  return {
@@ -911,7 +911,6 @@ export interface ManagedSubject {
911
911
  session: string | null;
912
912
  state: State;
913
913
  subject_id: string;
914
- successor: string | null;
915
914
  }
916
915
  /**
917
916
  * Exact spawn-edge child identity required by `spawn.close`.
@@ -1323,7 +1322,6 @@ export interface LifecycleObservation {
1323
1322
  session: string | null;
1324
1323
  state: State;
1325
1324
  subject_id: string;
1326
- successor: string | null;
1327
1325
  }
1328
1326
  /**
1329
1327
  * A runtime pane: location plus optional display text.
@@ -1452,6 +1450,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
1452
1450
  * nothing to verify.
1453
1451
  */
1454
1452
  freshness?: SpawnListPageSerializeFreshness | null;
1453
+ granted_at?: string | null;
1455
1454
  launched: SpawnListPageSerializeRequestOutcome;
1456
1455
  location?: SpawnListPageSerializeRegistryLocation | null;
1457
1456
  parent: string;
@@ -1461,6 +1460,7 @@ export interface SpawnListPageSerializePublicSpawnTopology {
1461
1460
  request_id: string;
1462
1461
  state: SpawnListPageSerializeSpawnState;
1463
1462
  version: number;
1463
+ withdrawn?: SpawnListPageSerializeWithdrawn | null;
1464
1464
  }
1465
1465
  /**
1466
1466
  * The child a token bound: the registering incarnation's own registry identity.
@@ -1497,3 +1497,11 @@ export interface SpawnListPageSerializeRegistryLocation {
1497
1497
  tab?: string | null;
1498
1498
  workspace?: string | null;
1499
1499
  }
1500
+ /**
1501
+ * Why and when a launch ended without ever presenting, so a reader never infers
1502
+ * failure from absence.
1503
+ */
1504
+ export interface SpawnListPageSerializeWithdrawn {
1505
+ at: string;
1506
+ reason: string;
1507
+ }