pi-radar 0.7.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/README.md CHANGED
@@ -35,6 +35,18 @@ The client uses `registry.v1` for registry operations and additionally requires
35
35
 
36
36
  The handoff API stores immutable bounded content separately from the recipient receipt. `inbox()` returns pending IDs only; retrieve each payload with `get()` and persist it before `ack()`, so a batch cannot exceed the RPC response bound. A worker's first Herdsman handoff is addressed only to the stable `parent_subject_id` recorded by Radar for its accepted managed launch. The parent gets its stable subject ID from `RadarSubject` and supplies it as `RadarWorkerLaunch.parent.subject_id`; `launchRadarWorker` forwards it as `managed.parent_subject_id`, and Radar injects the accepted value into the worker as `PI_RADAR_MANAGED_PARENT_SUBJECT_ID`. The handoff port reads that process context; callers cannot choose a destination. The daemon checks it against the durable parent record. Do not substitute a registration ID, Pi session, owner-session field, pane, or inferred lineage; unmanaged/session routing is deferred. The owner must durably capture the payload locally before calling `ack`; acknowledgement does not settle Herdsman assignment state or authorize worker closure. Pending inbox scans recover work after daemon restart or missed pointer events, and acknowledged content remains retrievable during the retention window. See `docs/control-plane.md` for wire shapes, bounds and recovery semantics.
37
37
 
38
+ ## Launching a worker
39
+
40
+ `launchRadarWorker` takes the parent from the caller's own `publication`, so a consumer passes the pane it splits and no registration or subject for the parent. The result names the child by its `subject_id`, the key that stays the same when the child restarts or resumes; keep that as the child's identifier. A received handoff carries its sender's `subject_id`; match children by it.
41
+
42
+ To start a released child again, pass `continues: subject_id`: the daemon takes the session from its own record, so no session id or path is sent, and it refuses a child that is not yours or is still running. `client.restart` replaces your own live child under a new program. Both need Radar 0.4.0 or newer.
43
+
44
+ A launch is ready when the daemon grants the child's first presence, and `failed` when the daemon withdraws it. The daemon owns a 30-second deadline and records the withdrawal on the launch, so `failed` means the launch is already cleaned up and a retry is safe. `launchRadarWorker` has no timeout of its own; pass a `signal` to stop waiting. `unresolved` is left for a launch whose outcome cannot be read at all. `children.restart(subject, program)` replaces a live child in its own pane and waits on the same outcome.
45
+
46
+ ## Addressing children by subject
47
+
48
+ The runtime's `children` service (`RADAR_RUNTIME_VERSION` 4) is the surface for everything after a launch. `list()` returns the caller's children as `{ subject_id, request_id, pane_id, state, exited }`; `observe`, `inspect`, `assignment` and `retire` take the subject. pi-radar resolves a subject to its newest bound launch on every call and never remembers the process, so a child replaced under the same subject is reached by the same key. A held assignment follows a replaced child: the next `list`, `observe` or `inspect` finds the new process and moves the assignment onto it, retiring it from the old one. Consumers need no registration or claim id.
49
+
38
50
  ## Generated protocol contracts
39
51
 
40
52
  The selected Rust wire DTOs are the source for the draft 2020-12 schemas in `schema/public/v1/`, shipped and exported under `pi-radar/schema/`. They are this package's contract of record: a daemon change lands as a schema regeneration, and the method-to-schema index lives in `docs/agent-registration.md` in the Radar repository. Serialize and deserialize schemas are separate because Serde defaults, null handling and omission differ by direction. The SDK's ping, Pi presence report/answer/release, handoff inbox, publication-parameter, immutable handoff, receipt, managed-launch-metadata input, facts-input, spawn-exit, and observation request/snapshot/response types are generated into `extension/generated-contracts.ts`; do not edit that file by hand.
@@ -8,12 +8,13 @@ import { lstatSync } from "node:fs";
8
8
  import { createConnection } from "node:net";
9
9
  import { dirname, join } from "node:path";
10
10
  var RADAR_PROTOCOL_VERSION = 2;
11
- var RADAR_MINIMUM_REVISION = 6;
11
+ var RADAR_MINIMUM_REVISION = 8;
12
12
  var RADAR_REGISTRY_CONTRACT = "registry.v1";
13
13
  var RADAR_REGISTRY_FACTS_CONTRACT = "registry.facts.v1";
14
14
  var RADAR_SPAWN_CONTRACT = "spawn.v1";
15
15
  var RADAR_SPAWN_LIST_CONTRACT = "spawn.list.v1";
16
16
  var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
17
+ var RADAR_LIFECYCLE_RESTART_CONTRACT = "lifecycle.restart.v1";
17
18
  var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
18
19
  var RADAR_EVENTS_CONTRACT = "events.v1";
19
20
  var RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
@@ -315,7 +316,11 @@ function spawnEdge(value) {
315
316
  const source = stringAt(bound, "source");
316
317
  const incarnation = stringAt(bound, "incarnation");
317
318
  const agentId = stringAt(bound, "registration_id");
319
+ const subjectId = stringAt(bound, "subject_id");
318
320
  const exit = exitAt(recordAt(value, "exit_observation"));
321
+ const withdrawnRecord = recordAt(value, "withdrawn");
322
+ const withdrawnAt = stringAt(withdrawnRecord, "at");
323
+ const withdrawnReason = stringAt(withdrawnRecord, "reason");
319
324
  return {
320
325
  ok: true,
321
326
  value: {
@@ -328,10 +333,13 @@ function spawnEdge(value) {
328
333
  bound: {
329
334
  source,
330
335
  incarnation,
331
- ...isUuid(agentId) ? { registration_id: agentId } : {}
336
+ ...isUuid(agentId) ? { registration_id: agentId } : {},
337
+ ...isUuid(subjectId) ? { subject_id: subjectId } : {}
332
338
  }
333
339
  } : {},
334
- ...exit !== void 0 ? { exit_observation: exit } : {}
340
+ ...exit !== void 0 ? { exit_observation: exit } : {},
341
+ ...stringAt(value, "granted_at") !== void 0 ? { granted_at: stringAt(value, "granted_at") } : {},
342
+ ...withdrawnAt !== void 0 && withdrawnReason !== void 0 ? { withdrawn: { at: withdrawnAt, reason: withdrawnReason } } : {}
335
343
  }
336
344
  };
337
345
  }
@@ -400,6 +408,8 @@ function spawnTopology(value) {
400
408
  function spawnRequestShape(request) {
401
409
  if (!isUuid(request.request_id))
402
410
  return "spawn requires a canonical request UUID";
411
+ if (request.continue_subject_id !== void 0 && !isUuid(request.continue_subject_id))
412
+ return "continue_subject_id must be a canonical UUID";
403
413
  if (!isUuid(request.parent))
404
414
  return "spawn requires the parent's canonical agent UUID";
405
415
  if (!isUuid(request.parent_session_id))
@@ -808,7 +818,8 @@ function createRadarClient(options = {}) {
808
818
  argv: request.argv,
809
819
  ...request.cwd !== void 0 ? { cwd: request.cwd } : {},
810
820
  ...carriesContext && request.env !== void 0 ? { env: request.env } : {},
811
- ...request.managed !== void 0 ? { managed: request.managed } : {}
821
+ ...request.managed !== void 0 ? { managed: request.managed } : {},
822
+ ...request.continue_subject_id !== void 0 ? { continue_subject_id: request.continue_subject_id } : {}
812
823
  },
813
824
  requester
814
825
  },
@@ -836,6 +847,37 @@ function createRadarClient(options = {}) {
836
847
  }
837
848
  );
838
849
  },
850
+ async restart(request) {
851
+ if (!isUuid(request.request_id) || !isUuid(request.parent) || !isUuid(request.subject_id))
852
+ return {
853
+ ok: false,
854
+ code: "bad_params",
855
+ message: "restart requires canonical request, parent and subject UUIDs"
856
+ };
857
+ return withContract(RADAR_LIFECYCLE_RESTART_CONTRACT, async () => {
858
+ const answer = await call(
859
+ "lifecycle.restart",
860
+ {
861
+ request: {
862
+ subject_id: request.subject_id,
863
+ parent: request.parent,
864
+ executable: request.executable,
865
+ argv: request.argv,
866
+ ...request.cwd !== void 0 ? { cwd: request.cwd } : {},
867
+ ...request.env !== void 0 ? { env: request.env } : {}
868
+ },
869
+ requester
870
+ },
871
+ request.request_id
872
+ );
873
+ if (answer.ok === false) return answer;
874
+ const settled = focusOutcome(recordAt(answer.value, "request"), request.request_id);
875
+ if (settled.ok === false) return settled;
876
+ const launch = recordAt(recordAt(answer.value, "request"), "launch_request_id");
877
+ if (!isUuid(launch)) return protocol("lifecycle.restart reply names no launch");
878
+ return { ok: true, value: { ...settled.value, launch_request_id: launch } };
879
+ });
880
+ },
839
881
  async spawnList(request) {
840
882
  const registrationId = processRegistrationId;
841
883
  if (registrationId === void 0)
@@ -1203,6 +1245,339 @@ function boundedRadarSnapshot(snapshot) {
1203
1245
  };
1204
1246
  }
1205
1247
 
1248
+ // extension/radar-children.ts
1249
+ import { randomUUID as randomUUID2 } from "node:crypto";
1250
+
1251
+ // extension/radar-inspection.ts
1252
+ var INSPECTION_LINES = 80;
1253
+ var INSPECTION_OUTPUT_BYTES = 16 * 1024;
1254
+ async function provedRead(client, target) {
1255
+ const read = await client.agentGet(target.agentId);
1256
+ return read.ok === true && read.value.verification === "verified" && read.value.location?.pane === target.paneId && (target.session === void 0 || read.value.contextSession === target.session) ? read.value : void 0;
1257
+ }
1258
+ async function inspectRadarChild(client, target) {
1259
+ const before = await provedRead(client, target);
1260
+ if (before === void 0)
1261
+ throw new Error("Inspection target identity did not match");
1262
+ const output = await client.output({
1263
+ pane_id: target.paneId,
1264
+ source: "recent_unwrapped",
1265
+ lines: INSPECTION_LINES
1266
+ });
1267
+ if (output.ok === false)
1268
+ throw new Error(
1269
+ `Inspection output unavailable (${output.code}: ${output.message})`
1270
+ );
1271
+ const foreground = await client.processInfo({ pane_id: target.paneId });
1272
+ const after = await provedRead(client, target);
1273
+ if (after === void 0)
1274
+ throw new Error("Inspection target changed during capture");
1275
+ const lines = output.value.text.trim().split(/\r?\n/).slice(-INSPECTION_LINES);
1276
+ const bounded2 = utf8Tail(lines.join("\n"), INSPECTION_OUTPUT_BYTES);
1277
+ return Object.freeze({
1278
+ capturedAt: Date.now(),
1279
+ recentOutputTruncated: output.value.truncated || bounded2.truncated,
1280
+ ...bounded2.text ? { recentOutput: bounded2.text } : {},
1281
+ ...foreground.ok === true ? { foreground: foreground.value } : {},
1282
+ ...after.activity !== void 0 ? { activity: after.activity } : {}
1283
+ });
1284
+ }
1285
+ function utf8Tail(value, maxBytes) {
1286
+ const bytes = Buffer.from(value);
1287
+ if (bytes.length <= maxBytes) return { text: value, truncated: false };
1288
+ let start = bytes.length - maxBytes;
1289
+ while (start < bytes.length && (bytes[start] & 192) === 128) start++;
1290
+ return { text: bytes.subarray(start).toString(), truncated: true };
1291
+ }
1292
+
1293
+ // extension/radar-launch.ts
1294
+ import { setTimeout as delay } from "node:timers/promises";
1295
+ function unknownChild(reason) {
1296
+ return { kind: "unknown", diagnostic: trajectory(reason) };
1297
+ }
1298
+ function trajectory(reason) {
1299
+ return `the daemon's current record of this launch's child is ${reason}`;
1300
+ }
1301
+ async function observeRadarChild(client, provenance) {
1302
+ if (provenance.registration_id === void 0)
1303
+ return unknownChild("not bound to a child by this launch");
1304
+ if (provenance.pane_id === void 0)
1305
+ return unknownChild("without a pane this launch recorded for it");
1306
+ const read = await client.spawnGet(provenance.request_id);
1307
+ if (read.ok === false)
1308
+ return unknownChild(
1309
+ `not readable (${read.code}: ${read.message})`
1310
+ );
1311
+ const topology = read.value;
1312
+ const edge = topology.edge;
1313
+ if (edge.bound === void 0 || edge.bound.registration_id === void 0)
1314
+ return unknownChild("not bound to a child by the daemon");
1315
+ if (edge.bound.registration_id !== provenance.registration_id)
1316
+ return unknownChild(
1317
+ `bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
1318
+ );
1319
+ if (edge.location?.pane === void 0)
1320
+ return unknownChild("recorded without a pane");
1321
+ if (edge.location.pane !== provenance.pane_id)
1322
+ return unknownChild(
1323
+ `recorded on pane ${edge.location.pane}, not on ${provenance.pane_id}`
1324
+ );
1325
+ if (edge.exit_observation?.state !== "unknown")
1326
+ return unknownChild("recorded with an observed process exit");
1327
+ if (topology.freshness !== "fresh")
1328
+ return unknownChild(
1329
+ "on a pane the daemon's own inventory does not currently report"
1330
+ );
1331
+ if (topology.state !== "bound")
1332
+ return unknownChild(
1333
+ `not currently placed (state ${topology.state ?? "unreported"})`
1334
+ );
1335
+ const reply = await client.agentGet(edge.bound.registration_id);
1336
+ if (reply.ok === false)
1337
+ return unknownChild(
1338
+ `process verdict not readable (${reply.code}: ${reply.message})`
1339
+ );
1340
+ const { verification, location, contextSession } = reply.value;
1341
+ if (verification === "absent" || verification === "mismatched")
1342
+ return {
1343
+ kind: "gone",
1344
+ verification,
1345
+ diagnostic: trajectory(`the registered process is ${verification}`)
1346
+ };
1347
+ if (verification !== "verified")
1348
+ return unknownChild(`process verdict is ${verification}`);
1349
+ if (location?.pane !== provenance.pane_id)
1350
+ return unknownChild(
1351
+ `registered on ${location?.pane ?? "no pane"}, not on ${provenance.pane_id}`
1352
+ );
1353
+ if (provenance.session_id === void 0)
1354
+ return unknownChild("without a session this launch recorded for it");
1355
+ if (contextSession !== provenance.session_id)
1356
+ return unknownChild(
1357
+ `publishing session ${contextSession ?? "none"}, not ${provenance.session_id}`
1358
+ );
1359
+ return {
1360
+ kind: "live",
1361
+ evidence: {
1362
+ request_id: provenance.request_id,
1363
+ registration_id: edge.bound.registration_id,
1364
+ source: edge.bound.source,
1365
+ incarnation: edge.bound.incarnation,
1366
+ pane_id: edge.location.pane
1367
+ }
1368
+ };
1369
+ }
1370
+ function refusedLaunch(message) {
1371
+ return { kind: "failed", message };
1372
+ }
1373
+ function unresolvedLaunch(requestId, message) {
1374
+ return {
1375
+ kind: "unresolved",
1376
+ request_id: requestId,
1377
+ message: `spawn ${requestId} is unresolved (${message})`
1378
+ };
1379
+ }
1380
+ var PRE_EFFECT_CODES = [
1381
+ "absent",
1382
+ "unsupported",
1383
+ "bad_version",
1384
+ "bad_request",
1385
+ "bad_params",
1386
+ "unknown_method",
1387
+ "refused",
1388
+ "not_found",
1389
+ "busy",
1390
+ "backend_unavailable"
1391
+ ];
1392
+ function launchFailure(result, requestId) {
1393
+ return PRE_EFFECT_CODES.includes(result.code) ? refusedLaunch(result.message) : unresolvedLaunch(requestId, result.message);
1394
+ }
1395
+ async function waitForChild(client, requestId, options) {
1396
+ let last = "the daemon has not answered";
1397
+ for (; ; ) {
1398
+ if (options.signal?.aborted)
1399
+ return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
1400
+ const answer = await client.spawnGet(requestId);
1401
+ if (answer.ok === true) {
1402
+ const { edge } = answer.value;
1403
+ if (edge.withdrawn !== void 0)
1404
+ return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
1405
+ if (edge.granted_at === void 0) {
1406
+ await delay(options.pollMs);
1407
+ continue;
1408
+ }
1409
+ const pane = edge.location?.pane;
1410
+ if (pane === void 0 || pane === "")
1411
+ return unresolvedLaunch(
1412
+ requestId,
1413
+ "the daemon's launch recorded no pane to verify the child against"
1414
+ );
1415
+ const agentId = edge.bound?.registration_id;
1416
+ if (agentId !== void 0 && isUuid(agentId)) {
1417
+ const subjectId = edge.bound?.subject_id;
1418
+ if (subjectId === void 0 || !isUuid(subjectId))
1419
+ return unresolvedLaunch(
1420
+ requestId,
1421
+ `the daemon bound the child ${agentId} without recording its subject`
1422
+ );
1423
+ return {
1424
+ kind: "bound",
1425
+ subject_id: subjectId,
1426
+ pane_id: pane
1427
+ };
1428
+ }
1429
+ last = "no child has registered with this launch's token yet";
1430
+ } else {
1431
+ return unresolvedLaunch(requestId, answer.message);
1432
+ }
1433
+ await delay(options.pollMs);
1434
+ }
1435
+ }
1436
+
1437
+ // extension/radar-children.ts
1438
+ var PAGE = 100;
1439
+ var MAX_PAGES = 20;
1440
+ function createRadarChildren(client, publication) {
1441
+ const held = /* @__PURE__ */ new Map();
1442
+ async function resolveAll() {
1443
+ const newest = /* @__PURE__ */ new Map();
1444
+ let after;
1445
+ for (let page = 0; page < MAX_PAGES; page++) {
1446
+ const read = await client.spawnList({ ...after !== void 0 ? { after } : {}, limit: PAGE });
1447
+ if (read.ok === false) return read;
1448
+ for (const edge of read.value.spawns) {
1449
+ const bound = edge.bound;
1450
+ if (!bound?.subject_id || !bound.registration_id || edge.granted_at == null || edge.withdrawn != null) continue;
1451
+ const at = Date.parse(bound.bound_at);
1452
+ if (Number.isNaN(at)) continue;
1453
+ const current = newest.get(bound.subject_id);
1454
+ if (current !== void 0 && current.at >= at) continue;
1455
+ newest.set(bound.subject_id, {
1456
+ at,
1457
+ registration_id: bound.registration_id,
1458
+ child: {
1459
+ subject_id: bound.subject_id,
1460
+ request_id: edge.request_id,
1461
+ ...edge.location?.pane ? { pane_id: edge.location.pane } : {},
1462
+ state: edge.state,
1463
+ exited: edge.exit_observation != null && edge.exit_observation.state !== "unknown"
1464
+ }
1465
+ });
1466
+ }
1467
+ if (!read.value.next) return { ok: true, value: newest };
1468
+ after = read.value.next;
1469
+ }
1470
+ return { ok: false, code: "protocol", message: "the caller's launches exceed the readable pages" };
1471
+ }
1472
+ async function resolve2(subject) {
1473
+ const all = await resolveAll();
1474
+ return all.ok ? all.value.get(subject) : void 0;
1475
+ }
1476
+ async function resolveAndFollow(subject) {
1477
+ const all = await resolveAll();
1478
+ if (!all.ok) return void 0;
1479
+ follow(all.value);
1480
+ return all.value.get(subject);
1481
+ }
1482
+ function follow(all) {
1483
+ for (const [subject, entry] of held) {
1484
+ const target = all.get(subject);
1485
+ if (target !== void 0 && entry.published !== target.registration_id)
1486
+ entry.publish?.(target);
1487
+ }
1488
+ }
1489
+ return {
1490
+ async list() {
1491
+ const all = await resolveAll();
1492
+ if (!all.ok) return all;
1493
+ follow(all.value);
1494
+ return { ok: true, value: [...all.value.values()].map((entry) => entry.child) };
1495
+ },
1496
+ async restart(subject, program, options) {
1497
+ const parent = await publication.subject();
1498
+ if (parent?.subject_id === void 0)
1499
+ return { kind: "failed", message: "this process has no Radar registration to restart a child under" };
1500
+ const request_id = randomUUID2();
1501
+ const requested = await client.restart({
1502
+ request_id,
1503
+ parent: parent.registration_id,
1504
+ subject_id: subject,
1505
+ ...program
1506
+ });
1507
+ if (requested.ok === false)
1508
+ return launchFailure(requested, request_id);
1509
+ const bound = await waitForChild(client, requested.value.launch_request_id, {
1510
+ pollMs: options?.pollMs ?? 250,
1511
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
1512
+ });
1513
+ if (bound.kind !== "bound") return bound;
1514
+ await resolveAndFollow(subject);
1515
+ return {
1516
+ kind: "ready",
1517
+ value: {
1518
+ request_id: requested.value.launch_request_id,
1519
+ subject_id: bound.subject_id,
1520
+ pane_id: bound.pane_id
1521
+ }
1522
+ };
1523
+ },
1524
+ assignment(subject, owner) {
1525
+ const existing = held.get(subject);
1526
+ if (existing !== void 0) return existing.writer;
1527
+ let latest;
1528
+ const entry = { writer: void 0 };
1529
+ const send = (target) => {
1530
+ if (latest === void 0) return;
1531
+ if (entry.published !== void 0 && entry.published !== target.registration_id)
1532
+ publication.retireAssignment(entry.published);
1533
+ entry.published = target.registration_id;
1534
+ publication.assignment(target.registration_id, owner).update(latest);
1535
+ };
1536
+ entry.publish = send;
1537
+ entry.writer = {
1538
+ update(snapshot) {
1539
+ latest = snapshot;
1540
+ void resolve2(subject).then((target) => {
1541
+ if (target !== void 0) send(target);
1542
+ });
1543
+ },
1544
+ retire() {
1545
+ latest = void 0;
1546
+ if (entry.published !== void 0) publication.retireAssignment(entry.published);
1547
+ entry.published = void 0;
1548
+ }
1549
+ };
1550
+ held.set(subject, entry);
1551
+ return entry.writer;
1552
+ },
1553
+ retire(subject) {
1554
+ held.get(subject)?.writer.retire();
1555
+ held.delete(subject);
1556
+ },
1557
+ async observe(subject, session_id) {
1558
+ const target = await resolveAndFollow(subject);
1559
+ if (target === void 0)
1560
+ return { kind: "unknown", diagnostic: "the daemon records no bound launch for this child" };
1561
+ return observeRadarChild(client, {
1562
+ request_id: target.child.request_id,
1563
+ pane_id: target.child.pane_id,
1564
+ registration_id: target.registration_id,
1565
+ session_id
1566
+ });
1567
+ },
1568
+ async inspect(subject, session_id) {
1569
+ const target = await resolveAndFollow(subject);
1570
+ if (target?.child.pane_id === void 0)
1571
+ throw new Error("Inspection target is not a bound child with a pane");
1572
+ return inspectRadarChild(client, {
1573
+ agentId: target.registration_id,
1574
+ paneId: target.child.pane_id,
1575
+ ...session_id !== void 0 ? { session: session_id } : {}
1576
+ });
1577
+ }
1578
+ };
1579
+ }
1580
+
1206
1581
  // extension/radar-communication.ts
1207
1582
  function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
1208
1583
  async function agentId() {
@@ -1241,7 +1616,7 @@ function createRadarHandoffPort(client, publication, parentSubjectId = () => pro
1241
1616
  }
1242
1617
 
1243
1618
  // extension/radar-publication.ts
1244
- import { createHash, randomUUID as randomUUID2 } from "node:crypto";
1619
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
1245
1620
  import {
1246
1621
  closeSync,
1247
1622
  constants,
@@ -1371,7 +1746,7 @@ function createPublicationStore(root) {
1371
1746
  ensureDirectory(directory);
1372
1747
  const temporary = join2(
1373
1748
  directory,
1374
- `.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID2()}.tmp`
1749
+ `.${name.slice(name.lastIndexOf("/") + 1)}.${randomUUID3()}.tmp`
1375
1750
  );
1376
1751
  const bytes = Buffer.from(JSON.stringify(value), "utf8");
1377
1752
  if (bytes.length > MAX_RECORD_BYTES)
@@ -1427,7 +1802,7 @@ function radarProcessSlot() {
1427
1802
  return host[PROCESS_SLOT];
1428
1803
  }
1429
1804
  function radarProcessIncarnation(slot = radarProcessSlot()) {
1430
- return slot.incarnation ??= randomUUID2();
1805
+ return slot.incarnation ??= randomUUID3();
1431
1806
  }
1432
1807
  function recordAt2(value, key) {
1433
1808
  if (typeof value !== "object" || value === null) return void 0;
@@ -1892,7 +2267,7 @@ function createPresenceReporter(input) {
1892
2267
  releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
1893
2268
  }
1894
2269
  standing = void 0;
1895
- state = { version: 2, session, claim_id: randomUUID2(), releases };
2270
+ state = { version: 2, session, claim_id: randomUUID3(), releases };
1896
2271
  save();
1897
2272
  stopped = false;
1898
2273
  void ensure();
@@ -1929,7 +2304,7 @@ var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
1929
2304
  function storeIdentity(store) {
1930
2305
  const known = STORE_IDENTITY.get(store);
1931
2306
  if (known !== void 0) return known;
1932
- const created = randomUUID2();
2307
+ const created = randomUUID3();
1933
2308
  STORE_IDENTITY.set(store, created);
1934
2309
  return created;
1935
2310
  }
@@ -2559,7 +2934,7 @@ async function resolveRadarProcessLocation(client, env = process.env) {
2559
2934
 
2560
2935
  // extension/radar-runtime.ts
2561
2936
  var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
2562
- var RADAR_RUNTIME_VERSION = 2;
2937
+ var RADAR_RUNTIME_VERSION = 4;
2563
2938
  function lazyRadarRuntime(start) {
2564
2939
  let pending;
2565
2940
  return {
@@ -2588,6 +2963,7 @@ function registerPiRadar(pi) {
2588
2963
  const runtime = lazyRadarRuntime(async () => ({
2589
2964
  publication: execution.publication,
2590
2965
  communication: createRadarHandoffPort(client, execution.publication),
2966
+ children: createRadarChildren(client, execution.publication),
2591
2967
  execution
2592
2968
  }));
2593
2969
  provideRadarRuntime(pi.events, runtime);