pi-radar 0.2.0 → 0.4.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.
Files changed (42) hide show
  1. package/README.md +10 -4
  2. package/dist/extension/baseline.js +333 -394
  3. package/dist/extension/radar-client.js +126 -11
  4. package/dist/extension/radar-communication.js +39 -0
  5. package/dist/extension/radar-execution.js +169 -382
  6. package/dist/extension/radar-launch.js +18 -13
  7. package/dist/extension/radar-publication.js +161 -298
  8. package/dist/extension/radar-runtime.js +1 -1
  9. package/dist/index.js +356 -407
  10. package/extension/baseline.ts +2 -0
  11. package/extension/generated-contracts.ts +1310 -10
  12. package/extension/radar-client.ts +191 -23
  13. package/extension/radar-communication.ts +66 -0
  14. package/extension/radar-execution.ts +11 -58
  15. package/extension/radar-launch.ts +29 -22
  16. package/extension/radar-publication.ts +211 -410
  17. package/extension/radar-runtime.ts +4 -1
  18. package/index.ts +1 -0
  19. package/package.json +9 -5
  20. package/schema/public/v1/handoff-inbox.deserialize.schema.json +18 -0
  21. package/schema/public/v1/handoff-inbox.serialize.schema.json +18 -0
  22. package/schema/public/v1/handoff-publish-params.deserialize.schema.json +33 -0
  23. package/schema/public/v1/handoff-publish-params.serialize.schema.json +33 -0
  24. package/schema/public/v1/handoff-receipt.deserialize.schema.json +66 -0
  25. package/schema/public/v1/handoff-receipt.serialize.schema.json +66 -0
  26. package/schema/public/v1/handoff.deserialize.schema.json +84 -0
  27. package/schema/public/v1/handoff.serialize.schema.json +84 -0
  28. package/schema/public/v1/observation-get-params.deserialize.schema.json +46 -0
  29. package/schema/public/v1/observation-get-params.serialize.schema.json +52 -0
  30. package/schema/public/v1/observation-get-response.deserialize.schema.json +2731 -0
  31. package/schema/public/v1/observation-get-response.serialize.schema.json +2862 -0
  32. package/schema/public/v1/observation-snapshot.deserialize.schema.json +2616 -0
  33. package/schema/public/v1/observation-snapshot.serialize.schema.json +2741 -0
  34. package/schema/public/v1/presence-answer.deserialize.schema.json +40 -0
  35. package/schema/public/v1/presence-answer.serialize.schema.json +40 -0
  36. package/schema/public/v1/presence-release.deserialize.schema.json +51 -0
  37. package/schema/public/v1/presence-release.serialize.schema.json +52 -0
  38. package/schema/public/v1/presence-report.deserialize.schema.json +149 -0
  39. package/schema/public/v1/presence-report.serialize.schema.json +157 -0
  40. package/extension/radar-session-marker.ts +0 -146
  41. package/schema/public/v1/launch-identity-marker.deserialize.schema.json +0 -32
  42. package/schema/public/v1/launch-identity-marker.serialize.schema.json +0 -32
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ var __export = (target, all) => {
8
8
  var radar_client_exports = {};
9
9
  __export(radar_client_exports, {
10
10
  RADAR_CALL_TIMEOUT_MS: () => RADAR_CALL_TIMEOUT_MS,
11
+ RADAR_COMMUNICATION_CONTRACT: () => RADAR_COMMUNICATION_CONTRACT,
11
12
  RADAR_EVENTS_CONTRACT: () => RADAR_EVENTS_CONTRACT,
12
13
  RADAR_EVENT_RETRY_MS: () => RADAR_EVENT_RETRY_MS,
13
14
  RADAR_MAX_AWAITED: () => RADAR_MAX_AWAITED,
@@ -45,6 +46,7 @@ var RADAR_SPAWN_CONTRACT = "spawn.v1";
45
46
  var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
46
47
  var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
47
48
  var RADAR_EVENTS_CONTRACT = "events.v1";
49
+ var RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
48
50
  var RADAR_REQUESTER = "pi-radar";
49
51
  var RADAR_MAX_LINE_BYTES = 1024 * 1024;
50
52
  var RADAR_CALL_TIMEOUT_MS = 2e3;
@@ -142,6 +144,25 @@ function recordAt(value, field) {
142
144
  function isUuid(value) {
143
145
  return typeof value === "string" && UUID.test(value);
144
146
  }
147
+ function decodePresenceAnswer(value) {
148
+ const registrationId = recordAt(value, "registration_id");
149
+ const claimId = recordAt(value, "claim_id");
150
+ const standing = recordAt(value, "standing");
151
+ const subjectId = recordAt(value, "subject_id");
152
+ if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
153
+ return protocol("presence reply carries no valid claim standing");
154
+ return { ok: true, value };
155
+ }
156
+ function managementAt(value) {
157
+ const field = recordAt(value, "management");
158
+ const state = stringAt(field, "state");
159
+ if (state === "managed") {
160
+ const subjectId = stringAt(field, "subject_id");
161
+ return subjectId === void 0 ? void 0 : { state, subject_id: subjectId };
162
+ }
163
+ if (state === "duplicate" || state === "pending" || state === "unmanaged") return { state };
164
+ return void 0;
165
+ }
145
166
  function stringAt(value, field) {
146
167
  if (typeof value !== "object" || value === null) return void 0;
147
168
  const found = value[field];
@@ -152,6 +173,28 @@ function numberAt(value, field) {
152
173
  const found = value[field];
153
174
  return typeof found === "number" && Number.isFinite(found) ? found : void 0;
154
175
  }
176
+ function handoffAt(value) {
177
+ const version = numberAt(value, "version");
178
+ const id = stringAt(value, "id");
179
+ const sender = recordAt(value, "sender");
180
+ const senderGeneration = numberAt(sender, "execution_generation");
181
+ const target = stringAt(value, "recipient_subject_id");
182
+ const summary = stringAt(value, "summary");
183
+ const content = stringAt(value, "content");
184
+ const published = numberAt(value, "published_at_unix_ms");
185
+ const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
186
+ if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "registration_id")) || !isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) || !isUuid(stringAt(sender, "execution_id")) || !Number.isSafeInteger(senderGeneration) || (senderGeneration ?? 0) < 1 || !isUuid(target) || summary === void 0 || content === void 0 || published === void 0 || !Number.isSafeInteger(published) || published < 0 || correlation !== void 0 && correlation !== null && (typeof correlation !== "string" || correlation.length === 0 || Buffer.byteLength(correlation, "utf8") > 256 || new RegExp("\\p{Cc}", "u").test(correlation))) return void 0;
187
+ return value;
188
+ }
189
+ function handoffReceiptAt(value) {
190
+ const version = numberAt(value, "version");
191
+ const handoffId = stringAt(value, "handoff_id");
192
+ const recipient = recordAt(value, "recipient");
193
+ const generation = numberAt(recipient, "execution_generation");
194
+ const received = numberAt(value, "received_at_unix_ms");
195
+ if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "registration_id")) || !isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) || !isUuid(stringAt(recipient, "execution_id")) || !Number.isSafeInteger(generation) || (generation ?? 0) < 1 || received === void 0 || !Number.isSafeInteger(received) || received < 0) return void 0;
196
+ return value;
197
+ }
155
198
  function writerBinding(value) {
156
199
  const handle = stringAt(value, "handle");
157
200
  const source = stringAt(value, "source");
@@ -261,7 +304,8 @@ var RADAR_SPAWN_TOKEN_ENV = "PI_RADAR_SPAWN_TOKEN";
261
304
  var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
262
305
  "PI_RADAR_MANAGED_SUBJECT_ID",
263
306
  "PI_RADAR_MANAGED_LAUNCH_ID",
264
- "PI_RADAR_MANAGED_EXECUTION_GENERATION"
307
+ "PI_RADAR_MANAGED_EXECUTION_GENERATION",
308
+ "PI_RADAR_MANAGED_PARENT_SUBJECT_ID"
265
309
  ]);
266
310
  function unresolvedSpawn(requestId, detail) {
267
311
  return {
@@ -306,7 +350,7 @@ function spawnEdge(value) {
306
350
  const bound = recordAt(value, "bound");
307
351
  const source = stringAt(bound, "source");
308
352
  const incarnation = stringAt(bound, "incarnation");
309
- const agentId = stringAt(bound, "agent_id");
353
+ const agentId = stringAt(bound, "registration_id");
310
354
  const exit = exitAt(recordAt(value, "exit_observation"));
311
355
  return {
312
356
  ok: true,
@@ -320,7 +364,7 @@ function spawnEdge(value) {
320
364
  bound: {
321
365
  source,
322
366
  incarnation,
323
- ...isUuid(agentId) ? { agent_id: agentId } : {}
367
+ ...isUuid(agentId) ? { registration_id: agentId } : {}
324
368
  }
325
369
  } : {},
326
370
  ...exit !== void 0 ? { exit_observation: exit } : {}
@@ -718,6 +762,22 @@ function createRadarClient(options = {}) {
718
762
  return {
719
763
  socketPath,
720
764
  ping,
765
+ async presence(request) {
766
+ return withRegistry(async () => {
767
+ const answer = await call("agent.presence", { ...request });
768
+ if (!answer.ok) return answer;
769
+ return decodePresenceAnswer(answer.value);
770
+ });
771
+ },
772
+ async releasePresence(request) {
773
+ return withRegistry(async () => {
774
+ const answer = await call("agent.release", { ...request });
775
+ if (!answer.ok) return answer;
776
+ const decoded = decodePresenceAnswer(answer.value);
777
+ if (!decoded.ok) return decoded;
778
+ return decoded.value.standing === "released" ? decoded : protocol("release reply did not confirm the claim release");
779
+ });
780
+ },
721
781
  async register(registration) {
722
782
  return withRegistry(async () => {
723
783
  const answer = await call("agent.register", {
@@ -733,15 +793,15 @@ function createRadarClient(options = {}) {
733
793
  });
734
794
  if (answer.ok === false) return answer;
735
795
  const record = answer.value.registration;
736
- const agentId = stringAt(record, "agent_id");
737
- if (!isUuid(agentId)) return protocol("registration reply carries no agent_id");
738
- return { ok: true, value: { agent_id: agentId } };
796
+ const agentId = stringAt(record, "registration_id");
797
+ if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
798
+ return { ok: true, value: { registration_id: agentId } };
739
799
  });
740
800
  },
741
801
  async acquire(request) {
742
802
  return withRegistryChannel(request.channel, async () => {
743
803
  const answer = await call("agent.acquire", {
744
- agent_id: request.agent_id,
804
+ registration_id: request.registration_id,
745
805
  channel: request.channel,
746
806
  publisher: request.publisher,
747
807
  ...request.replace !== void 0 ? { replace: request.replace } : {}
@@ -770,7 +830,7 @@ function createRadarClient(options = {}) {
770
830
  }
771
831
  return withRegistryChannel(request.channel, async () => {
772
832
  const answer = await call("agent.publish", {
773
- agent_id: request.agent_id,
833
+ registration_id: request.registration_id,
774
834
  channel: request.channel,
775
835
  writer_handle: request.writer_handle,
776
836
  sequence: request.sequence,
@@ -789,7 +849,7 @@ function createRadarClient(options = {}) {
789
849
  async context(request) {
790
850
  return withRegistry(async () => {
791
851
  const answer = await call("agent.context", {
792
- agent_id: request.agent_id,
852
+ registration_id: request.registration_id,
793
853
  publisher: request.publisher,
794
854
  ...request.writer_handle !== void 0 ? { writer_handle: request.writer_handle } : {},
795
855
  ...request.replace !== void 0 ? { replace: request.replace } : {},
@@ -805,9 +865,14 @@ function createRadarClient(options = {}) {
805
865
  if (writer === void 0)
806
866
  return protocol("context reply carries no writer binding");
807
867
  const warning = stringAt(answer.value, "warning");
868
+ const management = managementAt(answer.value);
808
869
  return {
809
870
  ok: true,
810
- value: { writer, ...warning !== void 0 ? { warning } : {} }
871
+ value: {
872
+ writer,
873
+ ...warning !== void 0 ? { warning } : {},
874
+ ...management !== void 0 ? { management } : {}
875
+ }
811
876
  };
812
877
  });
813
878
  },
@@ -884,7 +949,7 @@ function createRadarClient(options = {}) {
884
949
  message: "agent.get requires a canonical agent UUID"
885
950
  };
886
951
  return withRegistry(async () => {
887
- const answer = await call("agent.get", { agent_id: agentId });
952
+ const answer = await call("agent.get", { registration_id: agentId });
888
953
  if (answer.ok === false) return answer;
889
954
  return agentProcessAt(recordAt(answer.value, "agent"));
890
955
  });
@@ -935,6 +1000,56 @@ function createRadarClient(options = {}) {
935
1000
  return protocol("spawn.close reply carries no close outcome");
936
1001
  });
937
1002
  },
1003
+ async communicationPublish(request) {
1004
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
1005
+ const answer = await call("communication.publish", {
1006
+ sender_registration_id: request.sender_registration_id,
1007
+ recipient_subject_id: request.recipient_subject_id,
1008
+ ...request.correlation_id !== void 0 ? { correlation_id: request.correlation_id } : {},
1009
+ summary: request.summary,
1010
+ content: request.content
1011
+ }, request.request_id);
1012
+ if (answer.ok === false) return answer;
1013
+ const handoff = handoffAt(recordAt(answer.value, "handoff"));
1014
+ const created = recordAt(answer.value, "created");
1015
+ if (handoff === void 0 || typeof created !== "boolean")
1016
+ return protocol("communication.publish reply carries no handoff");
1017
+ return { ok: true, value: { handoff, created } };
1018
+ });
1019
+ },
1020
+ async communicationInbox(request) {
1021
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
1022
+ const answer = await call("communication.inbox", request);
1023
+ if (answer.ok === false) return answer;
1024
+ const handoffIds = recordAt(answer.value, "handoff_ids");
1025
+ if (!Array.isArray(handoffIds) || handoffIds.some((id) => !isUuid(id)))
1026
+ return protocol("communication.inbox reply carries malformed handoff IDs");
1027
+ return { ok: true, value: answer.value };
1028
+ });
1029
+ },
1030
+ async communicationGet(request) {
1031
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
1032
+ const answer = await call("communication.get", request);
1033
+ if (answer.ok === false) return answer;
1034
+ const handoff = handoffAt(recordAt(answer.value, "handoff"));
1035
+ const receiptValue = recordAt(answer.value, "receipt");
1036
+ const receipt = receiptValue === null ? null : handoffReceiptAt(receiptValue);
1037
+ if (handoff === void 0 || receiptValue !== null && receipt === void 0)
1038
+ return protocol("communication.get reply carries malformed content or receipt");
1039
+ return { ok: true, value: { handoff, receipt: receipt ?? null } };
1040
+ });
1041
+ },
1042
+ async communicationAck(request) {
1043
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
1044
+ const answer = await call("communication.ack", request);
1045
+ if (answer.ok === false) return answer;
1046
+ const receipt = handoffReceiptAt(recordAt(answer.value, "receipt"));
1047
+ const created = recordAt(answer.value, "created");
1048
+ if (receipt === void 0 || typeof created !== "boolean")
1049
+ return protocol("communication.ack reply carries no receipt");
1050
+ return { ok: true, value: { receipt, created } };
1051
+ });
1052
+ },
938
1053
  async events(request) {
939
1054
  if (!Number.isSafeInteger(request.after) || request.after < 0 || !Number.isSafeInteger(request.wait_ms) || request.wait_ms < 0 || request.wait_ms > RADAR_EVENT_WAIT_MS)
940
1055
  return {
@@ -1155,6 +1270,47 @@ function boundedRadarSnapshot(snapshot) {
1155
1270
  };
1156
1271
  }
1157
1272
 
1273
+ // extension/radar-communication.ts
1274
+ var radar_communication_exports = {};
1275
+ __export(radar_communication_exports, {
1276
+ createRadarHandoffPort: () => createRadarHandoffPort
1277
+ });
1278
+ function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
1279
+ async function agentId() {
1280
+ const subject = await publication.subject();
1281
+ return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.registration_id };
1282
+ }
1283
+ return {
1284
+ async publish(request) {
1285
+ const identity = await agentId();
1286
+ if (identity.ok === false) return identity;
1287
+ const parent = parentSubjectId();
1288
+ if (parent === void 0)
1289
+ return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
1290
+ return client.communicationPublish({
1291
+ ...request,
1292
+ sender_registration_id: identity.value,
1293
+ recipient_subject_id: parent
1294
+ });
1295
+ },
1296
+ async inbox() {
1297
+ const identity = await agentId();
1298
+ if (identity.ok === false) return identity;
1299
+ return client.communicationInbox({ registration_id: identity.value });
1300
+ },
1301
+ async get(handoff_id) {
1302
+ const identity = await agentId();
1303
+ if (identity.ok === false) return identity;
1304
+ return client.communicationGet({ registration_id: identity.value, handoff_id });
1305
+ },
1306
+ async ack(handoff_id) {
1307
+ const identity = await agentId();
1308
+ if (identity.ok === false) return identity;
1309
+ return client.communicationAck({ registration_id: identity.value, handoff_id });
1310
+ }
1311
+ };
1312
+ }
1313
+
1158
1314
  // extension/radar-execution.ts
1159
1315
  var radar_execution_exports = {};
1160
1316
  __export(radar_execution_exports, {
@@ -1193,77 +1349,7 @@ import {
1193
1349
  writeSync
1194
1350
  } from "node:fs";
1195
1351
  import { dirname as dirname2, join as join2, parse, resolve, sep } from "node:path";
1196
-
1197
- // extension/radar-session-marker.ts
1198
- var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
1199
- var RADAR_LAUNCH_MARKER_VERSION = 1;
1200
- var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
1201
- var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
1202
- var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
1203
- var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
1204
- var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
1205
- function decodeRadarLaunchMarker(value) {
1206
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1207
- const record = value;
1208
- if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
1209
- return void 0;
1210
- }
1211
- if (record.version !== RADAR_LAUNCH_MARKER_VERSION || typeof record.subject_id !== "string" || !UUID2.test(record.subject_id) || typeof record.launch_id !== "string" || !UUID2.test(record.launch_id) || !Number.isSafeInteger(record.execution_generation) || record.execution_generation < 1 || record.execution_generation > RADAR_MAX_EXECUTION_GENERATION) {
1212
- return void 0;
1213
- }
1214
- return {
1215
- version: RADAR_LAUNCH_MARKER_VERSION,
1216
- subject_id: record.subject_id,
1217
- launch_id: record.launch_id,
1218
- execution_generation: record.execution_generation
1219
- };
1220
- }
1221
- function appendRadarLaunchMarker(appendEntry, marker) {
1222
- const validated = decodeRadarLaunchMarker(marker);
1223
- if (!validated) throw new Error("invalid Radar launch identity marker");
1224
- appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
1225
- }
1226
- function latestRadarLaunchMarker(branch) {
1227
- let latest;
1228
- for (const entry of branch) {
1229
- if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
1230
- latest = entry;
1231
- }
1232
- }
1233
- if (!latest) return { status: "absent" };
1234
- const marker = decodeRadarLaunchMarker(latest.data);
1235
- return marker ? { status: "valid", marker } : { status: "invalid" };
1236
- }
1237
- function radarLaunchMarkerFromEnvironment(env = process.env) {
1238
- const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
1239
- if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
1240
- return void 0;
1241
- }
1242
- return decodeRadarLaunchMarker({
1243
- version: RADAR_LAUNCH_MARKER_VERSION,
1244
- subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
1245
- launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
1246
- execution_generation: Number(rawGeneration)
1247
- });
1248
- }
1249
- function activeRadarLaunchMarker(host) {
1250
- if (!host.sessionManager) return { status: "absent" };
1251
- return latestRadarLaunchMarker(host.sessionManager.getBranch());
1252
- }
1253
- function persistRadarLaunchMarker(host, env = process.env) {
1254
- const marker = radarLaunchMarkerFromEnvironment(env);
1255
- if (!marker) return "absent";
1256
- if (!host.appendEntry || !host.sessionManager) return "unsupported";
1257
- const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
1258
- if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
1259
- appendRadarLaunchMarker(host.appendEntry, marker);
1260
- return "written";
1261
- }
1262
- function sameMarker(a, b) {
1263
- return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
1264
- }
1265
-
1266
- // extension/radar-publication.ts
1352
+ var DUPLICATE_SESSION_DIAGNOSTIC = "Radar rejected this session claim as a duplicate; reload or resume the session before retrying";
1267
1353
  var RADAR_PUBLICATION_VERSION = 1;
1268
1354
  var RADAR_LEASE_MS = 3e4;
1269
1355
  var RADAR_HEARTBEAT_MS = 15e3;
@@ -1288,7 +1374,7 @@ function readProcessBirth() {
1288
1374
  return void 0;
1289
1375
  }
1290
1376
  }
1291
- var RECORD_NAME = /^(?:subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
1377
+ var RECORD_NAME = /^(?:registrations|presence|subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
1292
1378
  var MAX_RECORD_BYTES = 64 * 1024;
1293
1379
  var PRIVATE_MODE = 448;
1294
1380
  var RECORD_MODE = 384;
@@ -1573,39 +1659,6 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
1573
1659
  return void 0;
1574
1660
  return { sequence, snapshot, lease_ms: lease, observed_at: observed };
1575
1661
  }
1576
- var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
1577
- var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
1578
- var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
1579
- var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
1580
- function sessionAt(value) {
1581
- if (typeof value !== "object" || value === null) return void 0;
1582
- const found = value.session;
1583
- if (found === null) return null;
1584
- return typeof found === "string" ? found : void 0;
1585
- }
1586
- function sameLaunchIdentity(a, b) {
1587
- return a === b || a !== void 0 && b !== void 0 && a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
1588
- }
1589
- function contextReportAt(value, keys, markedKeys) {
1590
- const marked = exactKeys(value, markedKeys);
1591
- if (!exactKeys(value, keys) && !marked) return void 0;
1592
- const sequence = numberAt2(value, "sequence");
1593
- const lease = numberAt2(value, "lease_ms");
1594
- const observed = textAt(value, "observed_at");
1595
- const session = sessionAt(value);
1596
- if (sequence === void 0 || sequence < 1) return void 0;
1597
- if (lease === void 0 || observed === void 0 || session === void 0)
1598
- return void 0;
1599
- const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
1600
- if (marked && marker === void 0) return void 0;
1601
- return {
1602
- sequence,
1603
- session,
1604
- lease_ms: lease,
1605
- observed_at: observed,
1606
- ...marker !== void 0 ? { launch_identity: marker } : {}
1607
- };
1608
- }
1609
1662
  function createChannelWriter(input) {
1610
1663
  const { store, client, scheduler, recordName, channel, publisher } = input;
1611
1664
  const codec = input.codec ?? executionCodec;
@@ -1621,7 +1674,7 @@ function createChannelWriter(input) {
1621
1674
  function blank(agentId) {
1622
1675
  return {
1623
1676
  version: RADAR_PUBLICATION_VERSION,
1624
- agent_id: agentId,
1677
+ registration_id: agentId,
1625
1678
  channel,
1626
1679
  publisher,
1627
1680
  sequence: 0
@@ -1629,7 +1682,7 @@ function createChannelWriter(input) {
1629
1682
  }
1630
1683
  function loadRecord() {
1631
1684
  const value = store.read(recordName);
1632
- const agentId = textAt(value, "agent_id");
1685
+ const agentId = textAt(value, "registration_id");
1633
1686
  const storedPublisher = recordAt2(value, "publisher");
1634
1687
  if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || agentId === void 0 || !isUuid(agentId) || textAt(value, "channel") !== channel || textAt(storedPublisher, "source") !== publisher.source || textAt(storedPublisher, "incarnation") !== publisher.incarnation || textAt(storedPublisher, "reporting_owner") !== publisher.reporting_owner)
1635
1688
  return blank("");
@@ -1644,7 +1697,7 @@ function createChannelWriter(input) {
1644
1697
  const sentAt = numberAt2(acceptedValue, "sent_at");
1645
1698
  return {
1646
1699
  version: RADAR_PUBLICATION_VERSION,
1647
- agent_id: agentId,
1700
+ registration_id: agentId,
1648
1701
  channel,
1649
1702
  publisher,
1650
1703
  ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
@@ -1685,12 +1738,12 @@ function createChannelWriter(input) {
1685
1738
  if (record.writer !== void 0) return true;
1686
1739
  const agentId = await input.subject(recovering);
1687
1740
  if (agentId === void 0) return false;
1688
- if (record.agent_id !== agentId) {
1741
+ if (record.registration_id !== agentId) {
1689
1742
  record = blank(agentId);
1690
1743
  save();
1691
1744
  }
1692
1745
  const answer = await client.acquire({
1693
- agent_id: agentId,
1746
+ registration_id: agentId,
1694
1747
  channel,
1695
1748
  publisher
1696
1749
  });
@@ -1720,7 +1773,7 @@ function createChannelWriter(input) {
1720
1773
  const writer = record.writer;
1721
1774
  if (writer === void 0) return;
1722
1775
  const answer = await client.publish({
1723
- agent_id: record.agent_id,
1776
+ registration_id: record.registration_id,
1724
1777
  channel,
1725
1778
  writer_handle: writer.handle,
1726
1779
  sequence: pending.sequence,
@@ -1814,9 +1867,9 @@ function createChannelWriter(input) {
1814
1867
  stopPump();
1815
1868
  const writer = record.writer;
1816
1869
  if (writer === void 0) return;
1817
- const agentId = record.agent_id;
1870
+ const agentId = record.registration_id;
1818
1871
  try {
1819
- void client.retire({ agent_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
1872
+ void client.retire({ registration_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
1820
1873
  if (answer.ok === true) {
1821
1874
  try {
1822
1875
  record.retired_at = new Date(scheduler.now()).toISOString();
@@ -1839,214 +1892,127 @@ function createChannelWriter(input) {
1839
1892
  diagnostic: () => record.diagnostic
1840
1893
  };
1841
1894
  }
1842
- function createContextWriter(input) {
1843
- const { store, client, scheduler, recordName, publisher } = input;
1844
- let record = loadRecord();
1845
- let desired;
1846
- let desiredMarker;
1847
- let running = false;
1848
- let again = false;
1849
- let recovering = false;
1850
- let stopped = false;
1851
- let fenced = false;
1852
- let pump;
1853
- function blank(agentId) {
1854
- return {
1855
- version: RADAR_PUBLICATION_VERSION,
1856
- agent_id: agentId,
1857
- publisher,
1858
- sequence: 0
1859
- };
1860
- }
1861
- function loadRecord() {
1862
- const value = store.read(recordName);
1863
- const agentId = textAt(value, "agent_id");
1864
- const storedPublisher = recordAt2(value, "publisher");
1865
- if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || agentId === void 0 || !isUuid(agentId) && agentId !== "" || textAt(storedPublisher, "source") !== publisher.source || textAt(storedPublisher, "incarnation") !== publisher.incarnation || textAt(storedPublisher, "reporting_owner") !== publisher.reporting_owner)
1866
- return blank("");
1867
- const writer = recordAt2(value, "writer");
1868
- const handle = textAt(writer, "handle");
1869
- const generation = numberAt2(writer, "generation");
1870
- if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
1871
- return blank(agentId);
1872
- const pending = contextReportAt(
1873
- recordAt2(value, "pending"),
1874
- CONTEXT_PENDING_KEYS,
1875
- CONTEXT_PENDING_MARKED_KEYS
1876
- );
1877
- const acceptedValue = recordAt2(value, "accepted");
1878
- const accepted = contextReportAt(
1879
- acceptedValue,
1880
- CONTEXT_ACCEPTED_KEYS,
1881
- CONTEXT_ACCEPTED_MARKED_KEYS
1882
- );
1883
- const sentAt = numberAt2(acceptedValue, "sent_at");
1884
- return {
1885
- version: RADAR_PUBLICATION_VERSION,
1886
- agent_id: agentId,
1887
- publisher,
1888
- ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
1889
- sequence: numberAt2(value, "sequence") ?? 0,
1890
- ...pending !== void 0 ? { pending } : {},
1891
- ...accepted !== void 0 && sentAt !== void 0 ? { accepted: { ...accepted, sent_at: sentAt } } : {},
1892
- ...textAt(value, "diagnostic") !== void 0 ? { diagnostic: textAt(value, "diagnostic") } : {}
1893
- };
1894
- }
1895
- function note(message) {
1896
- if (record.diagnostic !== void 0) return;
1897
- record.diagnostic = message.slice(0, 256);
1898
- save();
1895
+ function createPresenceReporter(input) {
1896
+ const { store, client, scheduler, recordName } = input;
1897
+ let state;
1898
+ let standing;
1899
+ let diagnostic;
1900
+ try {
1901
+ const saved = store.read(recordName);
1902
+ if (saved?.version === 2 && isUuid(saved.claim_id) && isUuid(saved.session) && Array.isArray(saved.releases))
1903
+ state = saved;
1904
+ } catch (error) {
1905
+ diagnostic = `presence storage unavailable: ${String(error)}`;
1899
1906
  }
1907
+ let timer;
1908
+ let stopped = false;
1909
+ let running;
1900
1910
  function save() {
1901
- store.write(recordName, record);
1902
- }
1903
- function fence(message) {
1904
- fenced = true;
1905
- stopPump();
1906
- note(`context stopped: ${message}`);
1907
- }
1908
- function startPump() {
1909
- if (pump !== void 0 || stopped || fenced) return;
1910
- pump = scheduler.setInterval(() => kick(), input.heartbeatMs);
1911
- pump.unref?.();
1912
- }
1913
- function stopPump() {
1914
- if (pump === void 0) return;
1915
- scheduler.clearInterval(pump);
1916
- pump = void 0;
1911
+ try {
1912
+ if (state) store.write(recordName, state);
1913
+ return true;
1914
+ } catch (error) {
1915
+ diagnostic = `presence storage unavailable: ${String(error)}`;
1916
+ return false;
1917
+ }
1917
1918
  }
1918
- async function ensureSubject() {
1919
- if (record.writer !== void 0) return true;
1920
- const agentId = await input.subject(recovering);
1921
- if (agentId === void 0) return false;
1922
- if (record.agent_id !== agentId) {
1923
- record = blank(agentId);
1919
+ async function releasePending() {
1920
+ if (!state) return;
1921
+ const registration = await input.registration();
1922
+ if (!registration?.process) return;
1923
+ for (const release of [...state.releases]) {
1924
+ const answer = await client.releasePresence({ ...release, process: registration.process });
1925
+ if (!answer.ok) return;
1926
+ state.releases = state.releases.filter((r) => r.claim_id !== release.claim_id);
1924
1927
  save();
1925
1928
  }
1926
- return true;
1927
1929
  }
1928
- function heartbeatDue() {
1929
- return record.accepted !== void 0 && scheduler.now() - record.accepted.sent_at >= input.heartbeatMs;
1930
+ function schedule() {
1931
+ if (stopped || timer) return;
1932
+ timer = scheduler.setTimeout(() => {
1933
+ timer = void 0;
1934
+ void ensure();
1935
+ }, input.heartbeatMs);
1936
+ timer.unref?.();
1930
1937
  }
1931
- async function send(pending) {
1932
- const writer = record.writer;
1933
- const answer = await client.context({
1934
- agent_id: record.agent_id,
1935
- publisher,
1936
- // A first publish presents no handle: the daemon binds the writer the
1937
- // publisher already is, which is the binding this writer then keeps.
1938
- ...writer !== void 0 ? { writer_handle: writer.handle } : {},
1939
- sequence: pending.sequence,
1940
- lease_ms: pending.lease_ms,
1941
- observed_at: pending.observed_at,
1942
- context: {
1943
- session: pending.session,
1944
- ...pending.launch_identity !== void 0 ? { launch_identity: pending.launch_identity } : {}
1938
+ async function ensure() {
1939
+ if (running) return running;
1940
+ if (stopped || !state) return void 0;
1941
+ const claim = state;
1942
+ if (!save()) return void 0;
1943
+ running = (async () => {
1944
+ await releasePending();
1945
+ const registration = await input.registration();
1946
+ if (!registration?.process || state !== claim || stopped) return void 0;
1947
+ const answer = await client.presence({
1948
+ ...registration,
1949
+ process: registration.process,
1950
+ session_id: claim.session,
1951
+ claim_id: claim.claim_id,
1952
+ ...claim.registration_id ? { registration_id: claim.registration_id } : {},
1953
+ ...claim.subject_id ? { subject_id: claim.subject_id } : {}
1954
+ });
1955
+ if (state !== claim) return void 0;
1956
+ if (!answer.ok) {
1957
+ if (!["absent", "timeout", "transport"].includes(answer.code)) diagnostic = answer.message;
1958
+ return void 0;
1945
1959
  }
1946
- });
1947
- if (answer.ok === true) {
1948
- recovering = false;
1949
- record.writer = {
1950
- handle: answer.value.writer.handle,
1951
- generation: answer.value.writer.generation
1952
- };
1953
- record.sequence = Math.max(record.sequence, answer.value.writer.sequence);
1954
- record.accepted = {
1955
- ...pending,
1956
- sent_at: answer.value.warning === void 0 ? scheduler.now() : record.accepted?.sent_at ?? scheduler.now()
1957
- };
1958
- record.pending = void 0;
1960
+ claim.registration_id = answer.value.registration_id;
1961
+ claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
1962
+ standing = answer.value.standing === "granted" ? { state: "managed", subject_id: claim.subject_id } : answer.value.standing === "duplicate" ? { state: "duplicate" } : answer.value.standing === "pending" ? { state: "pending" } : { state: "unmanaged" };
1963
+ diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
1959
1964
  save();
1960
- return;
1961
- }
1962
- if (answer.code === "refused" || answer.code === "bad_params" || // A daemon that speaks the registry but does not serve this record would
1963
- // refuse every retry the same way; one diagnostic beats a silent retry
1964
- // loop once per heartbeat.
1965
- answer.code === "unknown_method") {
1966
- fence(answer.message);
1967
- return;
1968
- }
1969
- if (answer.code === "not_found") {
1970
- record = blank("");
1971
- recovering = true;
1972
- save();
1973
- return;
1974
- }
1975
- }
1976
- async function stage(session, launchIdentity) {
1977
- const pending = {
1978
- sequence: record.sequence + 1,
1979
- session,
1980
- lease_ms: input.leaseMs,
1981
- observed_at: new Date(scheduler.now()).toISOString(),
1982
- ...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
1983
- };
1984
- record.pending = pending;
1985
- save();
1986
- await send(pending);
1987
- }
1988
- async function drainOnce() {
1989
- if (fenced) return;
1990
- if (record.pending !== void 0) {
1991
- await send(record.pending);
1992
- return;
1993
- }
1994
- if (desired === void 0) return;
1995
- if (!await ensureSubject()) return;
1996
- if (fenced) return;
1997
- if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
1998
- await stage(desired, desiredMarker);
1999
- return;
2000
- }
2001
- if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
2002
- }
2003
- async function drain() {
2004
- if (running || stopped || fenced) return;
2005
- running = true;
1965
+ return claim.registration_id;
1966
+ })();
2006
1967
  try {
2007
- await drainOnce();
1968
+ return await running;
2008
1969
  } catch (error) {
2009
- fenced = true;
2010
- stopPump();
2011
- try {
2012
- note(`context storage failure: ${String(error).slice(0, 180)}`);
2013
- } catch {
2014
- }
1970
+ diagnostic = `presence unavailable: ${String(error)}`;
1971
+ return void 0;
2015
1972
  } finally {
2016
- running = false;
2017
- if (again && !stopped && !fenced) {
2018
- again = false;
2019
- kick();
2020
- }
1973
+ running = void 0;
1974
+ if (!stopped && state !== claim) void ensure();
1975
+ else schedule();
2021
1976
  }
2022
1977
  }
2023
- function kick() {
2024
- if (stopped || fenced) return;
2025
- startPump();
2026
- if (running) {
2027
- again = true;
2028
- return;
2029
- }
2030
- void drain().catch(() => void 0);
2031
- }
2032
1978
  return {
2033
- update(session, launchIdentity) {
2034
- if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
2035
- return;
2036
- desired = session;
2037
- desiredMarker = launchIdentity;
2038
- if (stopped) return;
2039
- kick();
1979
+ report(session) {
1980
+ const releases = [...state?.releases ?? []];
1981
+ if (state?.registration_id && !releases.some((release) => release.claim_id === state.claim_id)) {
1982
+ releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
1983
+ }
1984
+ standing = void 0;
1985
+ state = { version: 2, session, claim_id: randomUUID2(), releases };
1986
+ save();
1987
+ stopped = false;
1988
+ void ensure();
1989
+ },
1990
+ ensure,
1991
+ async release() {
1992
+ const releasing = state;
1993
+ standing = void 0;
1994
+ stopped = true;
1995
+ if (timer) scheduler.clearTimeout(timer);
1996
+ timer = void 0;
1997
+ if (running) await running;
1998
+ if (state === releasing) standing = void 0;
1999
+ if (releasing?.registration_id && !releasing.releases.some((r) => r.claim_id === releasing.claim_id)) {
2000
+ releasing.releases.push({ registration_id: releasing.registration_id, claim_id: releasing.claim_id });
2001
+ if (!save()) return;
2002
+ }
2003
+ await releasePending();
2040
2004
  },
2041
2005
  stop() {
2042
2006
  stopped = true;
2043
- stopPump();
2007
+ if (timer) scheduler.clearTimeout(timer);
2008
+ timer = void 0;
2044
2009
  },
2045
2010
  reopen() {
2046
2011
  stopped = false;
2047
- if (desired !== void 0 || record.pending !== void 0) kick();
2012
+ void ensure();
2048
2013
  },
2049
- diagnostic: () => record.diagnostic
2014
+ management: () => standing,
2015
+ diagnostic: () => diagnostic
2050
2016
  };
2051
2017
  }
2052
2018
  var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
@@ -2075,10 +2041,13 @@ function unavailablePublication(error) {
2075
2041
  };
2076
2042
  return {
2077
2043
  execution: writer,
2078
- context: {
2079
- update: () => void 0,
2044
+ presence: {
2045
+ report: () => void 0,
2046
+ ensure: async () => void 0,
2047
+ release: async () => void 0,
2080
2048
  stop: () => void 0,
2081
2049
  reopen: () => void 0,
2050
+ management: () => void 0,
2082
2051
  diagnostic: () => diagnostic
2083
2052
  },
2084
2053
  facts: { ...writer, update: () => void 0 },
@@ -2105,33 +2074,29 @@ function createRadarPublicationInner(options) {
2105
2074
  cached.reopen();
2106
2075
  return cached;
2107
2076
  }
2108
- const subjectName = `subjects/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2077
+ const registrationName = `registrations/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2109
2078
  const channelRecord = (channel, target) => `channels/${createHash("sha256").update(`${channel}\0${incarnation}\0${target ?? ""}`).digest("hex").slice(0, 32)}.json`;
2110
- const contextRecord = `context/${createHash("sha256").update(`context\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2111
- function readSubject() {
2112
- const value = store.read(subjectName);
2079
+ const presenceRecord = `presence/${createHash("sha256").update(`presence\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2080
+ function readRegistration() {
2081
+ const value = store.read(registrationName);
2113
2082
  const registration = recordAt2(value, "registration");
2114
2083
  if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || textAt(registration, "source") !== options.registration.source || textAt(registration, "incarnation") !== incarnation)
2115
2084
  return void 0;
2116
- const storedAgentId = textAt(value, "agent_id");
2085
+ const storedAgentId = textAt(value, "registration_id");
2117
2086
  return {
2118
2087
  version: RADAR_PUBLICATION_VERSION,
2119
2088
  registration,
2120
- ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { agent_id: storedAgentId } : {}
2089
+ ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { registration_id: storedAgentId } : {}
2121
2090
  };
2122
2091
  }
2123
- const stored = readSubject();
2124
- let subject = stored;
2092
+ const stored = readRegistration();
2093
+ let processRecord = stored;
2125
2094
  let registrationDiagnostic;
2126
2095
  let registrationRecord;
2127
2096
  async function ensureRegistration() {
2128
- if (subject !== void 0) return subject;
2097
+ if (processRecord !== void 0) return processRecord;
2129
2098
  registrationRecord ??= (async () => {
2130
2099
  const resolution = options.location ? await options.location() : { state: "absent", reason: "this process publishes no location" };
2131
- if (resolution.state === "pending") {
2132
- registrationDiagnostic = `location bootstrap pending: ${resolution.reason}`.slice(0, 256);
2133
- return void 0;
2134
- }
2135
2100
  const built = {
2136
2101
  version: RADAR_PUBLICATION_VERSION,
2137
2102
  registration: {
@@ -2142,13 +2107,13 @@ function createRadarPublicationInner(options) {
2142
2107
  }
2143
2108
  };
2144
2109
  try {
2145
- store.write(subjectName, built);
2110
+ store.write(registrationName, built);
2146
2111
  } catch (error) {
2147
2112
  registrationDiagnostic ??= `private storage unavailable: ${String(error)}`.slice(0, 256);
2148
2113
  return void 0;
2149
2114
  }
2150
2115
  registrationDiagnostic = void 0;
2151
- subject = built;
2116
+ processRecord = built;
2152
2117
  return built;
2153
2118
  })();
2154
2119
  try {
@@ -2157,11 +2122,10 @@ function createRadarPublicationInner(options) {
2157
2122
  registrationRecord = void 0;
2158
2123
  }
2159
2124
  }
2160
- let agentId = stored?.agent_id;
2125
+ let agentId = stored?.registration_id;
2161
2126
  let subjectReady;
2162
2127
  async function ensureSubject(recheck) {
2163
- if (recheck) agentId = void 0;
2164
- if (agentId !== void 0) return agentId;
2128
+ if (!recheck && agentId !== void 0) return agentId;
2165
2129
  subjectReady ??= (async () => {
2166
2130
  const ready = await client.ping();
2167
2131
  if (ready.ok === false) {
@@ -2171,18 +2135,11 @@ function createRadarPublicationInner(options) {
2171
2135
  }
2172
2136
  const record = await ensureRegistration();
2173
2137
  if (record === void 0) return void 0;
2174
- const answer = await client.register({
2175
- ...record.registration,
2176
- ...spawnToken !== void 0 ? { spawn_token: spawnToken } : {}
2177
- });
2178
- if (answer.ok === false) {
2179
- if (answer.code !== "absent" && answer.code !== "timeout" && answer.code !== "transport")
2180
- registrationDiagnostic ??= `subject registration refused: ${answer.message}`.slice(0, 256);
2181
- return void 0;
2182
- }
2183
- agentId = answer.value.agent_id;
2184
- subject = { ...record, agent_id: agentId };
2185
- store.write(subjectName, subject);
2138
+ const registered = await presence.ensure();
2139
+ if (registered === void 0) return void 0;
2140
+ agentId = registered;
2141
+ processRecord = { ...record, registration_id: agentId };
2142
+ store.write(registrationName, processRecord);
2186
2143
  if (options.binding !== void 0) writeBinding(options.binding);
2187
2144
  return agentId;
2188
2145
  })();
@@ -2196,7 +2153,7 @@ function createRadarPublicationInner(options) {
2196
2153
  if (agentId === void 0) return;
2197
2154
  store.write(`bindings/${binding.key}.json`, {
2198
2155
  version: RADAR_PUBLICATION_VERSION,
2199
- agent_id: agentId,
2156
+ registration_id: agentId,
2200
2157
  incarnation,
2201
2158
  run: binding.run,
2202
2159
  owner: binding.owner,
@@ -2220,14 +2177,22 @@ function createRadarPublicationInner(options) {
2220
2177
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2221
2178
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2222
2179
  });
2223
- const context = createContextWriter({
2180
+ const presence = createPresenceReporter({
2224
2181
  store,
2225
2182
  client,
2226
2183
  scheduler,
2227
- recordName: contextRecord,
2228
- publisher: publisher("herdsman-pi"),
2229
- subject: ensureSubject,
2230
- leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2184
+ recordName: presenceRecord,
2185
+ registration: async () => {
2186
+ const record = await ensureRegistration();
2187
+ if (!record) return void 0;
2188
+ const location = await options.location?.();
2189
+ const { location: _initialLocation, ...identity } = record.registration;
2190
+ return {
2191
+ ...identity,
2192
+ ...location?.state === "resolved" ? { location: location.location } : {},
2193
+ ...spawnToken ? { spawn_token: spawnToken } : {}
2194
+ };
2195
+ },
2231
2196
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2232
2197
  });
2233
2198
  const facts = createChannelWriter({
@@ -2245,7 +2210,7 @@ function createRadarPublicationInner(options) {
2245
2210
  const assignmentWriters = /* @__PURE__ */ new Map();
2246
2211
  const publication = {
2247
2212
  execution,
2248
- context,
2213
+ presence,
2249
2214
  facts,
2250
2215
  assignment(target, reportingOwner) {
2251
2216
  const existing = assignmentWriters.get(target);
@@ -2260,7 +2225,7 @@ function createRadarPublicationInner(options) {
2260
2225
  publisher: publisher("herdsman-owner", reportingOwner),
2261
2226
  codec: assignmentCodec,
2262
2227
  // The owner publishes about a subject it did not register: the exact
2263
- // agent_id from the child's sidecar is the whole correlation.
2228
+ // registration_id from the child's sidecar is the whole correlation.
2264
2229
  subject: async () => target,
2265
2230
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2266
2231
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
@@ -2280,7 +2245,7 @@ function createRadarPublicationInner(options) {
2280
2245
  registrationDiagnostic ??= `Radar binding unavailable: ${String(error).slice(0, 180)}`;
2281
2246
  return void 0;
2282
2247
  }
2283
- const bound = textAt(value, "agent_id");
2248
+ const bound = textAt(value, "registration_id");
2284
2249
  const boundIncarnation = textAt(value, "incarnation");
2285
2250
  const run = textAt(value, "run");
2286
2251
  const owner = textAt(value, "owner");
@@ -2290,7 +2255,7 @@ function createRadarPublicationInner(options) {
2290
2255
  return void 0;
2291
2256
  return {
2292
2257
  version: RADAR_PUBLICATION_VERSION,
2293
- agent_id: bound,
2258
+ registration_id: bound,
2294
2259
  incarnation: boundIncarnation,
2295
2260
  run,
2296
2261
  owner,
@@ -2306,23 +2271,27 @@ function createRadarPublicationInner(options) {
2306
2271
  if (agentId2 === void 0) return void 0;
2307
2272
  const record = await ensureRegistration();
2308
2273
  if (record === void 0) return void 0;
2309
- const location = record.registration.location;
2274
+ const currentLocation = await options.location?.();
2275
+ const location = currentLocation?.state === "resolved" ? currentLocation.location : void 0;
2276
+ const standing = presence.management();
2277
+ const subjectId = standing?.state === "managed" ? standing.subject_id : void 0;
2310
2278
  return {
2311
- agent_id: agentId2,
2279
+ registration_id: agentId2,
2280
+ ...subjectId !== void 0 ? { subject_id: subjectId } : {},
2312
2281
  ...location !== void 0 ? { location } : {}
2313
2282
  };
2314
2283
  },
2315
- diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? context.diagnostic() ?? facts.diagnostic(),
2284
+ diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? presence.diagnostic() ?? facts.diagnostic(),
2316
2285
  stop() {
2317
2286
  for (const writer of assignmentWriters.values()) writer.stop();
2318
2287
  execution.stop();
2319
- context.stop();
2288
+ presence.stop();
2320
2289
  facts.stop();
2321
2290
  },
2322
2291
  reopen() {
2323
2292
  for (const writer of assignmentWriters.values()) writer.reopen();
2324
2293
  execution.reopen();
2325
- context.reopen();
2294
+ presence.reopen();
2326
2295
  facts.reopen();
2327
2296
  }
2328
2297
  };
@@ -2360,10 +2329,13 @@ function unavailablePublication2() {
2360
2329
  return {
2361
2330
  execution: writer,
2362
2331
  facts: { ...writer, update: () => void 0 },
2363
- context: {
2364
- update: () => void 0,
2332
+ presence: {
2333
+ report: () => void 0,
2365
2334
  stop: () => void 0,
2366
2335
  reopen: () => void 0,
2336
+ management: () => void 0,
2337
+ ensure: async () => void 0,
2338
+ release: async () => void 0,
2367
2339
  diagnostic: () => "Radar private storage is unavailable"
2368
2340
  },
2369
2341
  assignment: () => writer,
@@ -2446,11 +2418,11 @@ function createRadarExecutionAdapter(pi, options) {
2446
2418
  ...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
2447
2419
  };
2448
2420
  }
2449
- function reportSession(context, launchIdentity) {
2421
+ function reportSession(context) {
2450
2422
  const session = context?.sessionManager?.getSessionId?.();
2451
2423
  if (!isUuid(session)) return;
2452
2424
  try {
2453
- publication.context.update(session, launchIdentity);
2425
+ publication.presence.report(session);
2454
2426
  } catch {
2455
2427
  }
2456
2428
  }
@@ -2514,38 +2486,8 @@ function createRadarExecutionAdapter(pi, options) {
2514
2486
  })
2515
2487
  );
2516
2488
  on("session_start", (_event, context) => {
2517
- const sessionHost = {
2518
- appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
2519
- sessionManager: context?.sessionManager
2520
- };
2521
- let launchIdentity;
2522
- try {
2523
- const result = persistRadarLaunchMarker(
2524
- sessionHost,
2525
- options.markerEnvironment
2526
- );
2527
- if (result === "written" || result === "unchanged") {
2528
- const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
2529
- const activeMarker = activeRadarLaunchMarker(sessionHost);
2530
- if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
2531
- launchIdentity = activeMarker.marker;
2532
- } else {
2533
- options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
2534
- }
2535
- } else if (result === "unsupported") {
2536
- options.diagnostic?.("Pi host does not support Radar launch identity session entries");
2537
- } else if (options.markerEnvironment && [
2538
- "PI_RADAR_MANAGED_SUBJECT_ID",
2539
- "PI_RADAR_MANAGED_LAUNCH_ID",
2540
- "PI_RADAR_MANAGED_EXECUTION_GENERATION"
2541
- ].some((key) => options.markerEnvironment?.[key] !== void 0)) {
2542
- options.diagnostic?.("incomplete or invalid Radar launch identity environment");
2543
- }
2544
- } catch (error) {
2545
- options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
2546
- }
2547
2489
  rootSession = true;
2548
- reportSession(context, launchIdentity);
2490
+ reportSession(context);
2549
2491
  publishFacts(context);
2550
2492
  publication.reopen();
2551
2493
  active = context?.isIdle?.() === false;
@@ -2576,8 +2518,9 @@ function createRadarExecutionAdapter(pi, options) {
2576
2518
  }
2577
2519
  emit();
2578
2520
  });
2579
- on("session_shutdown", () => {
2521
+ on("session_shutdown", async () => {
2580
2522
  suspend();
2523
+ await publication.presence.release();
2581
2524
  });
2582
2525
  function suspend() {
2583
2526
  if (stopped) return;
@@ -2785,7 +2728,7 @@ function trajectory(reason) {
2785
2728
  return `the daemon's current record of this launch's child is ${reason}`;
2786
2729
  }
2787
2730
  async function observeRadarChild(client, provenance) {
2788
- if (provenance.agent_id === void 0)
2731
+ if (provenance.registration_id === void 0)
2789
2732
  return unknownChild("not bound to a child by this launch");
2790
2733
  if (provenance.pane_id === void 0)
2791
2734
  return unknownChild("without a pane this launch recorded for it");
@@ -2796,11 +2739,11 @@ async function observeRadarChild(client, provenance) {
2796
2739
  );
2797
2740
  const topology = read.value;
2798
2741
  const edge = topology.edge;
2799
- if (edge.bound === void 0 || edge.bound.agent_id === void 0)
2742
+ if (edge.bound === void 0 || edge.bound.registration_id === void 0)
2800
2743
  return unknownChild("not bound to a child by the daemon");
2801
- if (edge.bound.agent_id !== provenance.agent_id)
2744
+ if (edge.bound.registration_id !== provenance.registration_id)
2802
2745
  return unknownChild(
2803
- `bound to another subject (${edge.bound.agent_id}), not to ${provenance.agent_id}`
2746
+ `bound to another subject (${edge.bound.registration_id}), not to ${provenance.registration_id}`
2804
2747
  );
2805
2748
  if (edge.location?.pane === void 0)
2806
2749
  return unknownChild("recorded without a pane");
@@ -2818,7 +2761,7 @@ async function observeRadarChild(client, provenance) {
2818
2761
  return unknownChild(
2819
2762
  `not currently placed (state ${topology.state ?? "unreported"})`
2820
2763
  );
2821
- const reply = await client.agentGet(edge.bound.agent_id);
2764
+ const reply = await client.agentGet(edge.bound.registration_id);
2822
2765
  if (reply.ok === false)
2823
2766
  return unknownChild(
2824
2767
  `process verdict not readable (${reply.code}: ${reply.message})`
@@ -2846,7 +2789,7 @@ async function observeRadarChild(client, provenance) {
2846
2789
  kind: "live",
2847
2790
  evidence: {
2848
2791
  request_id: provenance.request_id,
2849
- agent_id: edge.bound.agent_id,
2792
+ registration_id: edge.bound.registration_id,
2850
2793
  source: edge.bound.source,
2851
2794
  incarnation: edge.bound.incarnation,
2852
2795
  pane_id: edge.location.pane
@@ -2906,19 +2849,24 @@ async function launchRadarWorker(options) {
2906
2849
  const deadline = Date.now() + options.timeoutMs;
2907
2850
  const remaining = () => Math.max(0, deadline - Date.now());
2908
2851
  const pane = launch.parent.location?.pane;
2909
- if (!isUuid(launch.parent.agent_id))
2852
+ if (!isUuid(launch.parent.registration_id))
2910
2853
  return refusedLaunch(
2911
2854
  "the launch has no exact parent registration to spawn under"
2912
2855
  );
2856
+ if (!isUuid(launch.parent.subject_id))
2857
+ return refusedLaunch(
2858
+ "the launch has no managed Radar subject for its parent"
2859
+ );
2913
2860
  if (pane === void 0 || pane === "")
2914
2861
  return refusedLaunch(
2915
2862
  "this process's Radar registration names no pane, so it cannot be a spawn parent"
2916
2863
  );
2917
2864
  const spawned = await client.spawn({
2918
2865
  request_id: launch.request_id,
2919
- parent: launch.parent.agent_id,
2866
+ parent: launch.parent.registration_id,
2920
2867
  parent_session_id: launch.parent_session_id,
2921
2868
  executable: launch.executable,
2869
+ managed: { parent_subject_id: launch.parent.subject_id },
2922
2870
  argv: launch.argv,
2923
2871
  ...launch.cwd !== "" ? { cwd: launch.cwd } : {},
2924
2872
  ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
@@ -2940,10 +2888,10 @@ async function launchRadarWorker(options) {
2940
2888
  ...signal !== void 0 ? { signal } : {}
2941
2889
  });
2942
2890
  if (bound.kind !== "bound") return bound;
2943
- const agentId = bound.agent_id;
2891
+ const agentId = bound.registration_id;
2944
2892
  if (launch.onBound !== void 0) {
2945
2893
  try {
2946
- launch.onBound({ agent_id: agentId, pane_id: bound.pane_id });
2894
+ launch.onBound({ registration_id: agentId, pane_id: bound.pane_id });
2947
2895
  } catch (error) {
2948
2896
  return unresolvedLaunch(
2949
2897
  launch.request_id,
@@ -2971,7 +2919,7 @@ async function launchRadarWorker(options) {
2971
2919
  kind: "ready",
2972
2920
  value: {
2973
2921
  request_id: launch.request_id,
2974
- agent_id: agentId,
2922
+ registration_id: agentId,
2975
2923
  pane_id: bound.pane_id,
2976
2924
  bound_process: {
2977
2925
  source: bound.source,
@@ -3017,7 +2965,7 @@ async function waitForChild(client, requestId, options) {
3017
2965
  requestId,
3018
2966
  "the daemon observed the launched child's process exit"
3019
2967
  );
3020
- const agentId = edge.bound?.agent_id;
2968
+ const agentId = edge.bound?.registration_id;
3021
2969
  if (agentId !== void 0 && isUuid(agentId)) {
3022
2970
  const source = edge.bound?.source;
3023
2971
  const incarnation = edge.bound?.incarnation;
@@ -3028,7 +2976,7 @@ async function waitForChild(client, requestId, options) {
3028
2976
  );
3029
2977
  return {
3030
2978
  kind: "bound",
3031
- agent_id: agentId,
2979
+ registration_id: agentId,
3032
2980
  pane_id: pane,
3033
2981
  source,
3034
2982
  incarnation
@@ -3056,7 +3004,7 @@ __export(radar_runtime_exports, {
3056
3004
  requestRadarRuntime: () => requestRadarRuntime
3057
3005
  });
3058
3006
  var RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
3059
- var RADAR_RUNTIME_VERSION = 1;
3007
+ var RADAR_RUNTIME_VERSION = 2;
3060
3008
  function lazyRadarRuntime(start) {
3061
3009
  let pending;
3062
3010
  return {
@@ -3091,6 +3039,7 @@ function isRadarRuntimeHandle(value) {
3091
3039
  }
3092
3040
  export {
3093
3041
  radar_client_exports as client,
3042
+ radar_communication_exports as communication,
3094
3043
  radar_execution_exports as execution,
3095
3044
  radar_inspection_exports as inspection,
3096
3045
  radar_launch_exports as launch,