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.
@@ -5,135 +5,49 @@ import {
5
5
  type RadarErrorCode,
6
6
  type RadarLocation,
7
7
  type RadarResult,
8
- type RadarSpawnEdge,
9
8
  } from "./radar-client.ts";
10
- import { radarPaneHandle } from "./radar-location.ts";
9
+ import type { RadarPublication } from "./radar-publication.ts";
11
10
 
12
- /** The startup acknowledgement a launch waits for, named by its identity. */
13
- export type StartupExpectation = {
14
- generation: string;
15
- runId: string;
16
- ownerSessionId: string;
17
- workspaceId: string;
18
- agentLabel: string;
19
- paneId: string;
20
- };
21
-
22
- /**
23
- * The mailbox readers a launch needs. The mailbox belongs to the caller (Herdsman
24
- * supplies its own state shape and readers); this module only takes the path it
25
- * is handed and asks these questions of it.
26
- */
27
- export type LaunchState = { paneId: string };
28
-
29
- export type LaunchMailbox<State extends LaunchState> = {
30
- readState(path: string): State | undefined;
31
- waitForState(
32
- path: string,
33
- predicate: (state: State) => boolean,
34
- options: { timeoutMs: number; signal?: AbortSignal },
35
- ): Promise<State>;
36
- startupAckMatches(state: State | undefined, expected: StartupExpectation): boolean;
37
- };
38
-
39
- /**
40
- * Launching one fresh managed worker through Radar's spawn operation.
41
- *
42
- * A spawn is one durable effect behind the caller's own request UUID, and three
43
- * separate facts decide whether a child can take an assignment:
44
- *
45
- * 1. the daemon created a pane under this process's registered pane, launched
46
- * the child's exact command into it, and still reports that pane in its own
47
- * inventory;
48
- * 2. a child registered with the launch's private token, so the edge names the
49
- * exact `registration_id` that child obtained — never a label, pane or session;
50
- * 3. that child wrote a fresh startup acknowledgment for this launch generation
51
- * after Herdsman startup finished, naming the pane the daemon created as its
52
- * own location.
53
- *
54
- * Only the third is application readiness; the first two are prerequisites. A
55
- * lost reply is reconciled against the same request UUID and never re-sent, so a
56
- * failed launch cannot leave a second pane or a second child behind.
57
- *
58
- * The child's location is a fact about the pane the daemon created, not about
59
- * the environment it was launched with: the daemon passes an environment overlay
60
- * to a mux-created pane and cannot remove what that pane already carries, so a
61
- * child that inherited its parent's pane coordinates would claim the parent's
62
- * pane. The daemon's fresh record of the created pane and the child's own claim
63
- * must therefore name the same pane, and either one being stale, missing or
64
- * different leaves the launch unresolved rather than placed.
65
- */
66
-
67
- /** The exact managed parent this launch may use, and nothing inferred about it. */
68
- export type RadarLaunchParent = {
69
- registration_id: string;
70
- /** Stable subject from this process's current Radar registration. */
71
- subject_id: string;
72
- location?: RadarLocation;
73
- };
11
+ /** A launch succeeds when Radar grants its child's verified first presence. */
74
12
 
75
13
  /** Everything one fresh managed worker is launched with. */
76
14
  export type RadarWorkerLaunch = {
77
15
  /** The caller's durable key, persisted before dispatch and never replaced. */
78
16
  request_id: string;
79
- /** The nonce the child must repeat in its startup acknowledgment. */
80
- generation: string;
81
- parent: RadarLaunchParent;
17
+ /**
18
+ * Start this released child agent again rather than a new one. The daemon takes
19
+ * the session from its own record, so no session id or path is sent.
20
+ */
21
+ continues?: string;
22
+ /**
23
+ * The pane this launch splits. The parent is the calling process itself, read
24
+ * from its own publication, and must be registered on exactly this pane.
25
+ */
26
+ pane_id: string;
82
27
  parent_session_id: string;
83
28
  executable: string;
84
29
  argv: string[];
85
30
  cwd: string;
86
31
  /** The environment overlay the child needs, not a replacement environment. */
87
32
  env: Record<string, string>;
88
- /** The child's mailbox: its own state is the readiness evidence. */
89
- mailbox: string;
90
- /** The identity the child's state and acknowledgment must repeat exactly. */
91
- identity: {
92
- runId: string;
93
- ownerSessionId: string;
94
- workspaceId: string;
95
- agentLabel: string;
96
- };
97
- /**
98
- * The child's own published registration, once it has one. It is the second
99
- * witness that the edge's bound id is this launch's child, and it is read
100
- * through the owner's publication store rather than assumed from the pane.
101
- */
102
- observedChildAgent?: () => string | undefined;
103
- /**
104
- * Persist the daemon's own binding of this launch's edge, the subject it bound
105
- * and the pane it recorded, before readiness is waited for. This is the only
106
- * moment those facts are authoritative: the launch can end without ever
107
- * reaching an acknowledgment, and a reload in that window still has to read
108
- * where the daemon put this child. Throwing leaves the launch unresolved,
109
- * because a binding that was not persisted is one no later reader can hold the
110
- * child by.
111
- */
112
- onBound?: (bound: { registration_id: string; pane_id: string }) => void;
113
33
  };
114
34
 
115
- export type RadarWorkerLaunched<State = unknown> = {
35
+ export type RadarWorkerLaunched = {
116
36
  request_id: string;
117
- registration_id: string;
37
+ /** The child's durable identity: the key that survives its restart and resume. */
38
+ subject_id: string;
118
39
  pane_id: string;
119
- /**
120
- * The process the daemon bound this launch's edge to, taken from that same
121
- * answer: the source and incarnation the child registered under. They are what
122
- * an assignment to this child is addressed with, so the first task of a fresh
123
- * launch is addressed exactly as a retained continuation is.
124
- */
125
- bound_process: { source: string; incarnation: string };
126
- state: State;
127
40
  };
128
41
 
129
42
  /**
130
- * `refused` is a launch that provably did not begin; `unresolved` is one whose
131
- * pane, child or readiness cannot be established, which the caller reports
132
- * rather than retries.
43
+ * `ready` is a child whose first presence the daemon granted. `failed` is a launch
44
+ * the daemon withdrew (or that provably never began), so a retry is safe.
45
+ * `unresolved` is the rare launch whose outcome cannot be read at all, which the
46
+ * caller reports rather than retries.
133
47
  */
134
- export type RadarLaunchOutcome<State = unknown> =
135
- | { kind: "ready"; value: RadarWorkerLaunched<State> }
136
- | { kind: "refused"; message: string }
48
+ export type RadarLaunchOutcome =
49
+ | { kind: "ready"; value: RadarWorkerLaunched }
50
+ | { kind: "failed"; message: string }
137
51
  | { kind: "unresolved"; request_id: string; message: string };
138
52
 
139
53
  /**
@@ -178,8 +92,8 @@ function trajectory(reason: string): string {
178
92
  *
179
93
  * The durable spawn record and the child's own binding say *which* subject this
180
94
  * launch created; they are provenance, not liveness, and they stay true after a
181
- * child is gone. The startup acknowledgment says the child is ready, which is
182
- * also not liveness. Only a current topology read decides: `bound` has to name
95
+ * child is gone. A presence grant is startup evidence, not perpetual liveness.
96
+ * Only a current topology read decides: `bound` has to name
183
97
  * the same subject, the read has to record the pane this launch claims, no
184
98
  * process exit may have been observed, and the read's own `freshness` has to
185
99
  * report that the daemon's inventory currently places that pane. Anything else -
@@ -313,24 +227,25 @@ export async function relaunchRefusalForChild(
313
227
  return `the child's registered process is ${verification === "verified" ? "still running" : "not proved gone"}`;
314
228
  }
315
229
 
316
- export type RadarLaunchOptions<State extends LaunchState = LaunchState> = {
230
+ export type RadarLaunchOptions = {
317
231
  client: RadarClient;
318
- readers: LaunchMailbox<State>;
232
+ /** This process's own publication, which names the parent of the launch. */
233
+ publication: Pick<RadarPublication, "subject">;
319
234
  launch: RadarWorkerLaunch;
320
- timeoutMs: number;
321
- /** How often the binding and readiness waits re-read; 0 in driven tests. */
235
+ /** How often the wait re-reads the launch; 0 in driven tests. */
322
236
  pollMs?: number;
237
+ /** Stops the wait. The daemon's own deadline still withdraws the launch. */
323
238
  signal?: AbortSignal;
324
239
  };
325
240
 
326
- function refusedLaunch(message: string): Extract<RadarLaunchOutcome, { kind: "refused" | "unresolved" }> {
327
- return { kind: "refused", message };
241
+ function refusedLaunch(message: string): Extract<RadarLaunchOutcome, { kind: "failed" | "unresolved" }> {
242
+ return { kind: "failed", message };
328
243
  }
329
244
 
330
245
  function unresolvedLaunch(
331
246
  requestId: string,
332
247
  message: string,
333
- ): Extract<RadarLaunchOutcome, { kind: "refused" | "unresolved" }> {
248
+ ): Extract<RadarLaunchOutcome, { kind: "failed" | "unresolved" }> {
334
249
  return {
335
250
  kind: "unresolved",
336
251
  request_id: requestId,
@@ -338,19 +253,6 @@ function unresolvedLaunch(
338
253
  };
339
254
  }
340
255
 
341
- /** Whether the edge's own two effects already prove a launch happened. */
342
- function edgeStanding(edge: RadarSpawnEdge): "launched" | "refused" | "unknown" {
343
- if (edge.created === "refused" || edge.launched === "refused")
344
- return "refused";
345
- return edge.created === "completed" && edge.launched === "completed"
346
- ? "launched"
347
- : "unknown";
348
- }
349
-
350
- function edgeMessage(edge: RadarSpawnEdge): string {
351
- return `the daemon's spawn record is created ${edge.created} and launched ${edge.launched}`;
352
- }
353
-
354
256
  /**
355
257
  * The daemon's own refusals, which prove no effect was dispatched. Every other
356
258
  * failure — a lost reply, a timeout, an internal error, an unreadable answer —
@@ -371,155 +273,98 @@ const PRE_EFFECT_CODES: readonly RadarErrorCode[] = [
371
273
  "backend_unavailable",
372
274
  ];
373
275
 
374
- function spawnFailure(
375
- result: Extract<RadarResult<RadarSpawnEdge>, { ok: false }>,
276
+ export function launchFailure(
277
+ result: Extract<RadarResult<unknown>, { ok: false }>,
376
278
  requestId: string,
377
- ): Extract<RadarLaunchOutcome, { kind: "refused" | "unresolved" }> {
279
+ ): Extract<RadarLaunchOutcome, { kind: "failed" | "unresolved" }> {
378
280
  return PRE_EFFECT_CODES.includes(result.code)
379
281
  ? refusedLaunch(result.message)
380
282
  : unresolvedLaunch(requestId, result.message);
381
283
  }
382
284
 
383
285
  /**
384
- * Wait for the child's own registration to bind this edge, then for its startup
385
- * acknowledgment. The edge is re-read by the same request UUID: a spawn that
386
- * already answered is never sent again.
286
+ * Wait for the child's verified presence grant or the daemon's withdrawal. The
287
+ * same request UUID is read throughout; a lost reply never starts another child.
387
288
  */
388
- export async function launchRadarWorker<State extends LaunchState>(
389
- options: RadarLaunchOptions<State>,
390
- ): Promise<RadarLaunchOutcome<State>> {
289
+ export async function launchRadarWorker(
290
+ options: RadarLaunchOptions,
291
+ ): Promise<RadarLaunchOutcome> {
391
292
  const { client, launch, signal } = options;
392
293
  const pollMs = options.pollMs ?? 250;
393
- const deadline = Date.now() + options.timeoutMs;
394
- const remaining = (): number => Math.max(0, deadline - Date.now());
395
- const pane = launch.parent.location?.pane;
396
- if (!isUuid(launch.parent.registration_id))
294
+ const parent = await options.publication.subject();
295
+ if (parent === undefined || !isUuid(parent.registration_id))
397
296
  return refusedLaunch(
398
- "the launch has no exact parent registration to spawn under",
297
+ "this process has no Radar registration, so a worker cannot be spawned under it",
399
298
  );
400
- if (!isUuid(launch.parent.subject_id))
299
+ if (parent.subject_id === undefined || !isUuid(parent.subject_id))
401
300
  return refusedLaunch(
402
- "the launch has no managed Radar subject for its parent",
301
+ "this process has no managed Radar subject, so a worker cannot be spawned under it",
403
302
  );
404
303
  // A spawn is created under the pane its parent registered, and this port
405
304
  // registers no location it was not given: without one there is nothing to
406
305
  // place the child beside, and a name or session lookup is not a substitute.
306
+ const pane = parent.location?.pane;
407
307
  if (pane === undefined || pane === "")
408
308
  return refusedLaunch(
409
- "this process's Radar registration names no pane, so it cannot be a spawn parent",
309
+ "this process's Radar registration names no pane, so a worker cannot be placed beside it",
310
+ );
311
+ if (pane !== launch.pane_id)
312
+ return refusedLaunch(
313
+ `this process's Radar registration places it in pane ${pane}, not in the pane this launch would split`,
410
314
  );
411
315
 
412
316
  const spawned = await client.spawn({
413
317
  request_id: launch.request_id,
414
- parent: launch.parent.registration_id,
318
+ parent: parent.registration_id,
415
319
  parent_session_id: launch.parent_session_id,
416
320
  executable: launch.executable,
417
- managed: { parent_subject_id: launch.parent.subject_id },
321
+ managed: { parent_subject_id: parent.subject_id },
322
+ ...(launch.continues !== undefined ? { continue_subject_id: launch.continues } : {}),
418
323
  argv: launch.argv,
419
324
  ...(launch.cwd !== "" ? { cwd: launch.cwd } : {}),
420
325
  ...(Object.keys(launch.env).length > 0 ? { env: launch.env } : {}),
421
326
  });
422
327
  if (spawned.ok === false) {
423
- const refusal = spawnFailure(spawned, launch.request_id);
424
- if (refusal.kind === "refused") return refusal;
425
- }
426
- if (spawned.ok === true) {
427
- const standing = edgeStanding(spawned.value);
428
- if (standing === "refused")
429
- return refusedLaunch(edgeMessage(spawned.value));
430
- if (standing === "unknown")
431
- return unresolvedLaunch(launch.request_id, edgeMessage(spawned.value));
328
+ const refusal = launchFailure(spawned, launch.request_id);
329
+ if (refusal.kind === "failed") return refusal;
432
330
  }
433
331
 
434
332
  const bound = await waitForChild(client, launch.request_id, {
435
- remaining,
436
333
  pollMs,
437
334
  ...(signal !== undefined ? { signal } : {}),
438
335
  });
439
336
  if (bound.kind !== "bound") return bound;
440
- const agentId = bound.registration_id;
441
- // The binding is recorded before the readiness wait, not after it: the child
442
- // may never acknowledge, and the subject and pane the daemon named are still
443
- // what a reload has to prove this child by.
444
- if (launch.onBound !== undefined) {
445
- try {
446
- launch.onBound({ registration_id: agentId, pane_id: bound.pane_id });
447
- } catch (error) {
448
- return unresolvedLaunch(
449
- launch.request_id,
450
- `the daemon's binding of the child ${agentId} could not be recorded (${String(error)})`,
451
- );
452
- }
453
- }
454
-
455
- try {
456
- const state = await options.readers.waitForState(
457
- launch.mailbox,
458
- (candidate) =>
459
- options.readers.startupAckMatches(candidate, {
460
- generation: launch.generation,
461
- runId: launch.identity.runId,
462
- ownerSessionId: launch.identity.ownerSessionId,
463
- workspaceId: launch.identity.workspaceId,
464
- agentLabel: launch.identity.agentLabel,
465
- // The child writes the raw TMUX_PANE of the pane it runs in. Requiring
466
- // the pane the daemon created is what refuses a child that inherited
467
- // another process's coordinates.
468
- paneId: radarPaneHandle(bound.pane_id),
469
- }) &&
470
- (launch.observedChildAgent === undefined ||
471
- launch.observedChildAgent() === agentId),
472
- { timeoutMs: remaining(), ...(signal !== undefined ? { signal } : {}) },
473
- );
474
- return {
475
- kind: "ready",
476
- value: {
477
- request_id: launch.request_id,
478
- registration_id: agentId,
479
- pane_id: bound.pane_id,
480
- bound_process: {
481
- source: bound.source,
482
- incarnation: bound.incarnation,
483
- },
484
- state,
485
- },
486
- };
487
- } catch (error) {
488
- const claimed = options.readers.readState(launch.mailbox)?.paneId;
489
- return unresolvedLaunch(
490
- launch.request_id,
491
- claimed !== undefined && claimed !== radarPaneHandle(bound.pane_id)
492
- ? `the bound child ${agentId} claims the pane ${claimed}, not the pane ${bound.pane_id} this launch created for it`
493
- : `the bound child ${agentId} has not acknowledged this launch's startup (${String(error)})`,
494
- );
495
- }
337
+ return {
338
+ kind: "ready",
339
+ value: {
340
+ request_id: launch.request_id,
341
+ subject_id: bound.subject_id,
342
+ pane_id: bound.pane_id,
343
+ },
344
+ };
496
345
  }
497
346
 
498
347
  type BoundChild =
499
348
  | {
500
349
  kind: "bound";
501
- registration_id: string;
350
+ subject_id: string;
502
351
  pane_id: string;
503
- source: string;
504
- incarnation: string;
505
352
  }
506
- | Extract<RadarLaunchOutcome, { kind: "refused" | "unresolved" }>;
353
+ | Extract<RadarLaunchOutcome, { kind: "failed" | "unresolved" }>;
507
354
 
508
355
  /**
509
- * Poll the edge this request id authored until a child presents its token.
356
+ * Poll the edge this request id authored until the daemon grants its child or
357
+ * withdraws the launch. There is no timeout here: the daemon owns the deadline and
358
+ * records a withdrawal on the edge, so waiting longer than it would only wait for
359
+ * an outcome that is already decided.
510
360
  *
511
- * The daemon binds the edge once, to the exact registration the token produced,
512
- * so `bound.registration_id` is the child identity. A refusal or an unknown launch
513
- * effect ends the wait at once: neither becomes a child by waiting longer. An
514
- * edge whose recorded pane the daemon no longer reports ends it too: waiting
515
- * cannot create the pane a launch claims, and a location read afterwards would
516
- * be that of whatever moved in.
361
+ * Registration binding alone is not success: a pending or duplicate presence
362
+ * can bind an edge without ever acquiring attachment authority.
517
363
  */
518
- async function waitForChild(
519
- client: RadarClient,
364
+ export async function waitForChild(
365
+ client: Pick<RadarClient, "spawnGet">,
520
366
  requestId: string,
521
367
  options: {
522
- remaining: () => number;
523
368
  pollMs: number;
524
369
  signal?: AbortSignal;
525
370
  },
@@ -530,69 +375,41 @@ async function waitForChild(
530
375
  return unresolvedLaunch(requestId, `the launch was cancelled (${last})`);
531
376
  const answer = await client.spawnGet(requestId);
532
377
  if (answer.ok === true) {
533
- const { edge, state, freshness } = answer.value;
534
- const standing = edgeStanding(edge);
535
- if (standing === "refused")
536
- return refusedLaunch(`${edgeMessage(edge)}`);
537
- if (standing === "unknown")
538
- return unresolvedLaunch(requestId, edgeMessage(edge));
378
+ const { edge } = answer.value;
379
+ if (edge.withdrawn !== undefined)
380
+ return refusedLaunch(`the daemon withdrew this launch: ${edge.withdrawn.reason}`);
381
+ if (edge.granted_at === undefined) {
382
+ await delay(options.pollMs);
383
+ continue;
384
+ }
539
385
  const pane = edge.location?.pane;
540
386
  if (pane === undefined || pane === "")
541
387
  return unresolvedLaunch(
542
388
  requestId,
543
389
  "the daemon's launch recorded no pane to verify the child against",
544
390
  );
545
- // The recorded pane is the daemon's own statement about where it created
546
- // and launched this child, and `fresh` is its inventory confirming that
547
- // pane right now. Without that confirmation the launch has no verified
548
- // location to give the child, and no later read can supply one.
549
- if (freshness !== "fresh")
550
- return unresolvedLaunch(
551
- requestId,
552
- state === "unresolved"
553
- ? `the daemon's inventory does not report the pane ${pane} its own record names for this child`
554
- : `the daemon cannot confirm the pane ${pane} it recorded for this child`,
555
- );
556
- // An observed exit is the child's process, and a pane's shell can outlive it.
557
- if (edge.exit_observation?.state === "exited")
558
- return unresolvedLaunch(
559
- requestId,
560
- "the daemon observed the launched child's process exit",
561
- );
391
+
562
392
  const agentId = edge.bound?.registration_id;
563
393
  if (agentId !== undefined && isUuid(agentId)) {
564
- // A child the daemon bound without the process it registered as is a
565
- // child no assignment can be addressed to, and an unaddressed Radar
566
- // assignment is exactly what addressing exists to prevent: the launch
567
- // stays unresolved rather than degrading to a V5 request.
568
- const source = edge.bound?.source;
569
- const incarnation = edge.bound?.incarnation;
570
- if (
571
- source === undefined ||
572
- source === "" ||
573
- incarnation === undefined ||
574
- incarnation === ""
575
- )
394
+ // The edge names the agent as well as its process. Without it the child
395
+ // has no durable key to be returned by, and a registration is never
396
+ // offered in its place.
397
+ const subjectId = edge.bound?.subject_id;
398
+ if (subjectId === undefined || !isUuid(subjectId))
576
399
  return unresolvedLaunch(
577
400
  requestId,
578
- `the daemon bound the child ${agentId} without recording the process it registered as`,
401
+ `the daemon bound the child ${agentId} without recording its subject`,
579
402
  );
580
403
  return {
581
404
  kind: "bound",
582
- registration_id: agentId,
405
+ subject_id: subjectId,
583
406
  pane_id: pane,
584
- source,
585
- incarnation,
586
407
  };
587
408
  }
588
409
  last = "no child has registered with this launch's token yet";
589
410
  } else {
590
- last = answer.message;
411
+ return unresolvedLaunch(requestId, answer.message);
591
412
  }
592
- if (options.remaining() <= 0)
593
- return unresolvedLaunch(requestId, last);
594
- await delay(options.pollMs, undefined, {
595
- ...(options.signal !== undefined ? { signal: options.signal } : {}),
596
- });
413
+ await delay(options.pollMs);
597
414
  }
598
415
  }
@@ -1,10 +1,11 @@
1
1
  import type { RadarExecutionAdapter } from "./radar-execution.ts";
2
+ import type { RadarChildren } from "./radar-children.ts";
2
3
  import type { RadarHandoffPort } from "./radar-communication.ts";
3
4
  import type { RadarPublication } from "./radar-publication.ts";
4
5
 
5
6
  /** The event channel through which Pi extensions obtain the baseline runtime. */
6
7
  export const RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
7
- export const RADAR_RUNTIME_VERSION = 2 as const;
8
+ export const RADAR_RUNTIME_VERSION = 4 as const;
8
9
 
9
10
  /** The sole baseline publisher's session-scoped runtime, shared with consumers. */
10
11
  export type RadarRuntimeServices = {
@@ -12,6 +13,8 @@ export type RadarRuntimeServices = {
12
13
  publication: RadarPublication;
13
14
  /** Durable handoffs bound to this process's registration and managed parent. */
14
15
  communication: RadarHandoffPort;
16
+ /** This process's own children, addressed by subject. */
17
+ children: RadarChildren;
15
18
  execution: RadarExecutionAdapter;
16
19
  };
17
20
 
package/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * as children from "./extension/radar-children.ts";
1
2
  export * as client from "./extension/radar-client.ts";
2
3
  export * as communication from "./extension/radar-communication.ts";
3
4
  export * as execution from "./extension/radar-execution.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-radar",
3
- "version": "0.7.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Radar daemon client, communication, publication, execution, location, launch and inspection surface for Pi extensions.",
6
6
  "license": "Apache-2.0",
@@ -23,6 +23,10 @@
23
23
  "types": "./index.ts",
24
24
  "import": "./dist/index.js"
25
25
  },
26
+ "./children": {
27
+ "types": "./extension/radar-children.ts",
28
+ "import": "./dist/extension/radar-children.js"
29
+ },
26
30
  "./client": {
27
31
  "types": "./extension/radar-client.ts",
28
32
  "import": "./dist/extension/radar-client.js"
@@ -72,6 +76,7 @@
72
76
  "README.md",
73
77
  "schema/public/v1/**",
74
78
  "extension/generated-contracts.ts",
79
+ "extension/radar-children.ts",
75
80
  "extension/radar-client.ts",
76
81
  "extension/radar-communication.ts",
77
82
  "extension/radar-execution.ts",
@@ -83,10 +88,10 @@
83
88
  "extension/baseline.ts"
84
89
  ],
85
90
  "scripts": {
86
- "build": "esbuild index.ts extension/generated-contracts.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts extension/radar-runtime.ts extension/baseline.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
91
+ "build": "esbuild index.ts extension/generated-contracts.ts extension/radar-children.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts extension/radar-runtime.ts extension/baseline.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
87
92
  "generate:contracts": "node tools/generate-contracts.mjs",
88
93
  "check:contracts": "node tools/generate-contracts.mjs --check",
89
- "typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node index.ts extension/baseline.ts extension/generated-contracts.ts extension/generated-contracts.typecheck.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-launch.test.ts extension/radar-publication.ts extension/radar-runtime.ts extension/radar-runtime.test.ts",
94
+ "typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node index.ts extension/baseline.ts extension/generated-contracts.ts extension/generated-contracts.typecheck.ts extension/radar-children.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-launch.test.ts extension/radar-publication.ts extension/radar-runtime.ts extension/radar-runtime.test.ts",
90
95
  "prepack": "npm run build",
91
96
  "test": "npm run check:contracts && npm run typecheck:contracts && node --experimental-strip-types --test extension/*.test.ts"
92
97
  },
@@ -893,12 +893,6 @@
893
893
  },
894
894
  "subject_id": {
895
895
  "type": "string"
896
- },
897
- "successor": {
898
- "type": [
899
- "string",
900
- "null"
901
- ]
902
896
  }
903
897
  },
904
898
  "required": [
@@ -1060,12 +1054,6 @@
1060
1054
  },
1061
1055
  "subject_id": {
1062
1056
  "type": "string"
1063
- },
1064
- "successor": {
1065
- "type": [
1066
- "string",
1067
- "null"
1068
- ]
1069
1057
  }
1070
1058
  },
1071
1059
  "required": [
@@ -2448,7 +2436,7 @@
2448
2436
  },
2449
2437
  {
2450
2438
  "const": "captured",
2451
- "description": "The bootstrap's process is captured and released to exec.",
2439
+ "description": "The launch's process is captured and has been given its program.",
2452
2440
  "type": "string"
2453
2441
  },
2454
2442
  {
@@ -941,12 +941,6 @@
941
941
  },
942
942
  "subject_id": {
943
943
  "type": "string"
944
- },
945
- "successor": {
946
- "type": [
947
- "string",
948
- "null"
949
- ]
950
944
  }
951
945
  },
952
946
  "required": [
@@ -954,8 +948,7 @@
954
948
  "bound_registration_id",
955
949
  "process",
956
950
  "state",
957
- "session",
958
- "successor"
951
+ "session"
959
952
  ],
960
953
  "type": "object"
961
954
  },
@@ -1119,12 +1112,6 @@
1119
1112
  },
1120
1113
  "subject_id": {
1121
1114
  "type": "string"
1122
- },
1123
- "successor": {
1124
- "type": [
1125
- "string",
1126
- "null"
1127
- ]
1128
1115
  }
1129
1116
  },
1130
1117
  "required": [
@@ -1135,8 +1122,7 @@
1135
1122
  "metadata",
1136
1123
  "child_close",
1137
1124
  "state",
1138
- "session",
1139
- "successor"
1125
+ "session"
1140
1126
  ],
1141
1127
  "type": "object"
1142
1128
  },
@@ -2567,7 +2553,7 @@
2567
2553
  },
2568
2554
  {
2569
2555
  "const": "captured",
2570
- "description": "The bootstrap's process is captured and released to exec.",
2556
+ "description": "The launch's process is captured and has been given its program.",
2571
2557
  "type": "string"
2572
2558
  },
2573
2559
  {