pi-radar 0.3.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 (36) hide show
  1. package/README.md +4 -2
  2. package/dist/extension/baseline.js +227 -402
  3. package/dist/extension/radar-client.js +53 -13
  4. package/dist/extension/radar-communication.js +5 -5
  5. package/dist/extension/radar-execution.js +168 -383
  6. package/dist/extension/radar-launch.js +13 -13
  7. package/dist/extension/radar-publication.js +160 -299
  8. package/dist/index.js +239 -414
  9. package/extension/generated-contracts.ts +1249 -13
  10. package/extension/radar-client.ts +92 -29
  11. package/extension/radar-communication.ts +5 -5
  12. package/extension/radar-execution.ts +11 -58
  13. package/extension/radar-launch.ts +21 -21
  14. package/extension/radar-publication.ts +209 -412
  15. package/package.json +3 -4
  16. package/schema/public/v1/handoff-publish-params.deserialize.schema.json +2 -2
  17. package/schema/public/v1/handoff-publish-params.serialize.schema.json +2 -2
  18. package/schema/public/v1/handoff-receipt.deserialize.schema.json +4 -4
  19. package/schema/public/v1/handoff-receipt.serialize.schema.json +4 -4
  20. package/schema/public/v1/handoff.deserialize.schema.json +4 -4
  21. package/schema/public/v1/handoff.serialize.schema.json +4 -4
  22. package/schema/public/v1/observation-get-params.deserialize.schema.json +46 -0
  23. package/schema/public/v1/observation-get-params.serialize.schema.json +52 -0
  24. package/schema/public/v1/observation-get-response.deserialize.schema.json +2731 -0
  25. package/schema/public/v1/observation-get-response.serialize.schema.json +2862 -0
  26. package/schema/public/v1/observation-snapshot.deserialize.schema.json +2616 -0
  27. package/schema/public/v1/observation-snapshot.serialize.schema.json +2741 -0
  28. package/schema/public/v1/presence-answer.deserialize.schema.json +40 -0
  29. package/schema/public/v1/presence-answer.serialize.schema.json +40 -0
  30. package/schema/public/v1/presence-release.deserialize.schema.json +51 -0
  31. package/schema/public/v1/presence-release.serialize.schema.json +52 -0
  32. package/schema/public/v1/presence-report.deserialize.schema.json +149 -0
  33. package/schema/public/v1/presence-report.serialize.schema.json +157 -0
  34. package/extension/radar-session-marker.ts +0 -146
  35. package/schema/public/v1/launch-identity-marker.deserialize.schema.json +0 -32
  36. package/schema/public/v1/launch-identity-marker.serialize.schema.json +0 -32
package/dist/index.js CHANGED
@@ -144,6 +144,25 @@ function recordAt(value, field) {
144
144
  function isUuid(value) {
145
145
  return typeof value === "string" && UUID.test(value);
146
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
+ }
147
166
  function stringAt(value, field) {
148
167
  if (typeof value !== "object" || value === null) return void 0;
149
168
  const found = value[field];
@@ -164,7 +183,7 @@ function handoffAt(value) {
164
183
  const content = stringAt(value, "content");
165
184
  const published = numberAt(value, "published_at_unix_ms");
166
185
  const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
167
- if (version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "agent_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;
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;
168
187
  return value;
169
188
  }
170
189
  function handoffReceiptAt(value) {
@@ -173,7 +192,7 @@ function handoffReceiptAt(value) {
173
192
  const recipient = recordAt(value, "recipient");
174
193
  const generation = numberAt(recipient, "execution_generation");
175
194
  const received = numberAt(value, "received_at_unix_ms");
176
- if (version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "agent_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;
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;
177
196
  return value;
178
197
  }
179
198
  function writerBinding(value) {
@@ -331,7 +350,7 @@ function spawnEdge(value) {
331
350
  const bound = recordAt(value, "bound");
332
351
  const source = stringAt(bound, "source");
333
352
  const incarnation = stringAt(bound, "incarnation");
334
- const agentId = stringAt(bound, "agent_id");
353
+ const agentId = stringAt(bound, "registration_id");
335
354
  const exit = exitAt(recordAt(value, "exit_observation"));
336
355
  return {
337
356
  ok: true,
@@ -345,7 +364,7 @@ function spawnEdge(value) {
345
364
  bound: {
346
365
  source,
347
366
  incarnation,
348
- ...isUuid(agentId) ? { agent_id: agentId } : {}
367
+ ...isUuid(agentId) ? { registration_id: agentId } : {}
349
368
  }
350
369
  } : {},
351
370
  ...exit !== void 0 ? { exit_observation: exit } : {}
@@ -743,6 +762,22 @@ function createRadarClient(options = {}) {
743
762
  return {
744
763
  socketPath,
745
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
+ },
746
781
  async register(registration) {
747
782
  return withRegistry(async () => {
748
783
  const answer = await call("agent.register", {
@@ -758,15 +793,15 @@ function createRadarClient(options = {}) {
758
793
  });
759
794
  if (answer.ok === false) return answer;
760
795
  const record = answer.value.registration;
761
- const agentId = stringAt(record, "agent_id");
762
- if (!isUuid(agentId)) return protocol("registration reply carries no agent_id");
763
- 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 } };
764
799
  });
765
800
  },
766
801
  async acquire(request) {
767
802
  return withRegistryChannel(request.channel, async () => {
768
803
  const answer = await call("agent.acquire", {
769
- agent_id: request.agent_id,
804
+ registration_id: request.registration_id,
770
805
  channel: request.channel,
771
806
  publisher: request.publisher,
772
807
  ...request.replace !== void 0 ? { replace: request.replace } : {}
@@ -795,7 +830,7 @@ function createRadarClient(options = {}) {
795
830
  }
796
831
  return withRegistryChannel(request.channel, async () => {
797
832
  const answer = await call("agent.publish", {
798
- agent_id: request.agent_id,
833
+ registration_id: request.registration_id,
799
834
  channel: request.channel,
800
835
  writer_handle: request.writer_handle,
801
836
  sequence: request.sequence,
@@ -814,7 +849,7 @@ function createRadarClient(options = {}) {
814
849
  async context(request) {
815
850
  return withRegistry(async () => {
816
851
  const answer = await call("agent.context", {
817
- agent_id: request.agent_id,
852
+ registration_id: request.registration_id,
818
853
  publisher: request.publisher,
819
854
  ...request.writer_handle !== void 0 ? { writer_handle: request.writer_handle } : {},
820
855
  ...request.replace !== void 0 ? { replace: request.replace } : {},
@@ -830,9 +865,14 @@ function createRadarClient(options = {}) {
830
865
  if (writer === void 0)
831
866
  return protocol("context reply carries no writer binding");
832
867
  const warning = stringAt(answer.value, "warning");
868
+ const management = managementAt(answer.value);
833
869
  return {
834
870
  ok: true,
835
- value: { writer, ...warning !== void 0 ? { warning } : {} }
871
+ value: {
872
+ writer,
873
+ ...warning !== void 0 ? { warning } : {},
874
+ ...management !== void 0 ? { management } : {}
875
+ }
836
876
  };
837
877
  });
838
878
  },
@@ -909,7 +949,7 @@ function createRadarClient(options = {}) {
909
949
  message: "agent.get requires a canonical agent UUID"
910
950
  };
911
951
  return withRegistry(async () => {
912
- const answer = await call("agent.get", { agent_id: agentId });
952
+ const answer = await call("agent.get", { registration_id: agentId });
913
953
  if (answer.ok === false) return answer;
914
954
  return agentProcessAt(recordAt(answer.value, "agent"));
915
955
  });
@@ -963,7 +1003,7 @@ function createRadarClient(options = {}) {
963
1003
  async communicationPublish(request) {
964
1004
  return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
965
1005
  const answer = await call("communication.publish", {
966
- sender_agent_id: request.sender_agent_id,
1006
+ sender_registration_id: request.sender_registration_id,
967
1007
  recipient_subject_id: request.recipient_subject_id,
968
1008
  ...request.correlation_id !== void 0 ? { correlation_id: request.correlation_id } : {},
969
1009
  summary: request.summary,
@@ -1238,7 +1278,7 @@ __export(radar_communication_exports, {
1238
1278
  function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
1239
1279
  async function agentId() {
1240
1280
  const subject = await publication.subject();
1241
- return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.agent_id };
1281
+ return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.registration_id };
1242
1282
  }
1243
1283
  return {
1244
1284
  async publish(request) {
@@ -1249,24 +1289,24 @@ function createRadarHandoffPort(client, publication, parentSubjectId = () => pro
1249
1289
  return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
1250
1290
  return client.communicationPublish({
1251
1291
  ...request,
1252
- sender_agent_id: identity.value,
1292
+ sender_registration_id: identity.value,
1253
1293
  recipient_subject_id: parent
1254
1294
  });
1255
1295
  },
1256
1296
  async inbox() {
1257
1297
  const identity = await agentId();
1258
1298
  if (identity.ok === false) return identity;
1259
- return client.communicationInbox({ agent_id: identity.value });
1299
+ return client.communicationInbox({ registration_id: identity.value });
1260
1300
  },
1261
1301
  async get(handoff_id) {
1262
1302
  const identity = await agentId();
1263
1303
  if (identity.ok === false) return identity;
1264
- return client.communicationGet({ agent_id: identity.value, handoff_id });
1304
+ return client.communicationGet({ registration_id: identity.value, handoff_id });
1265
1305
  },
1266
1306
  async ack(handoff_id) {
1267
1307
  const identity = await agentId();
1268
1308
  if (identity.ok === false) return identity;
1269
- return client.communicationAck({ agent_id: identity.value, handoff_id });
1309
+ return client.communicationAck({ registration_id: identity.value, handoff_id });
1270
1310
  }
1271
1311
  };
1272
1312
  }
@@ -1309,77 +1349,7 @@ import {
1309
1349
  writeSync
1310
1350
  } from "node:fs";
1311
1351
  import { dirname as dirname2, join as join2, parse, resolve, sep } from "node:path";
1312
-
1313
- // extension/radar-session-marker.ts
1314
- var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
1315
- var RADAR_LAUNCH_MARKER_VERSION = 1;
1316
- var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
1317
- var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
1318
- var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
1319
- var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
1320
- 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;
1321
- function decodeRadarLaunchMarker(value) {
1322
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1323
- const record = value;
1324
- if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
1325
- return void 0;
1326
- }
1327
- 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) {
1328
- return void 0;
1329
- }
1330
- return {
1331
- version: RADAR_LAUNCH_MARKER_VERSION,
1332
- subject_id: record.subject_id,
1333
- launch_id: record.launch_id,
1334
- execution_generation: record.execution_generation
1335
- };
1336
- }
1337
- function appendRadarLaunchMarker(appendEntry, marker) {
1338
- const validated = decodeRadarLaunchMarker(marker);
1339
- if (!validated) throw new Error("invalid Radar launch identity marker");
1340
- appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
1341
- }
1342
- function latestRadarLaunchMarker(branch) {
1343
- let latest;
1344
- for (const entry of branch) {
1345
- if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
1346
- latest = entry;
1347
- }
1348
- }
1349
- if (!latest) return { status: "absent" };
1350
- const marker = decodeRadarLaunchMarker(latest.data);
1351
- return marker ? { status: "valid", marker } : { status: "invalid" };
1352
- }
1353
- function radarLaunchMarkerFromEnvironment(env = process.env) {
1354
- const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
1355
- if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
1356
- return void 0;
1357
- }
1358
- return decodeRadarLaunchMarker({
1359
- version: RADAR_LAUNCH_MARKER_VERSION,
1360
- subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
1361
- launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
1362
- execution_generation: Number(rawGeneration)
1363
- });
1364
- }
1365
- function activeRadarLaunchMarker(host) {
1366
- if (!host.sessionManager) return { status: "absent" };
1367
- return latestRadarLaunchMarker(host.sessionManager.getBranch());
1368
- }
1369
- function persistRadarLaunchMarker(host, env = process.env) {
1370
- const marker = radarLaunchMarkerFromEnvironment(env);
1371
- if (!marker) return "absent";
1372
- if (!host.appendEntry || !host.sessionManager) return "unsupported";
1373
- const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
1374
- if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
1375
- appendRadarLaunchMarker(host.appendEntry, marker);
1376
- return "written";
1377
- }
1378
- function sameMarker(a, b) {
1379
- return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
1380
- }
1381
-
1382
- // extension/radar-publication.ts
1352
+ var DUPLICATE_SESSION_DIAGNOSTIC = "Radar rejected this session claim as a duplicate; reload or resume the session before retrying";
1383
1353
  var RADAR_PUBLICATION_VERSION = 1;
1384
1354
  var RADAR_LEASE_MS = 3e4;
1385
1355
  var RADAR_HEARTBEAT_MS = 15e3;
@@ -1404,7 +1374,7 @@ function readProcessBirth() {
1404
1374
  return void 0;
1405
1375
  }
1406
1376
  }
1407
- 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$/;
1408
1378
  var MAX_RECORD_BYTES = 64 * 1024;
1409
1379
  var PRIVATE_MODE = 448;
1410
1380
  var RECORD_MODE = 384;
@@ -1689,39 +1659,6 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
1689
1659
  return void 0;
1690
1660
  return { sequence, snapshot, lease_ms: lease, observed_at: observed };
1691
1661
  }
1692
- var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
1693
- var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
1694
- var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
1695
- var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
1696
- function sessionAt(value) {
1697
- if (typeof value !== "object" || value === null) return void 0;
1698
- const found = value.session;
1699
- if (found === null) return null;
1700
- return typeof found === "string" ? found : void 0;
1701
- }
1702
- function sameLaunchIdentity(a, b) {
1703
- 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;
1704
- }
1705
- function contextReportAt(value, keys, markedKeys) {
1706
- const marked = exactKeys(value, markedKeys);
1707
- if (!exactKeys(value, keys) && !marked) return void 0;
1708
- const sequence = numberAt2(value, "sequence");
1709
- const lease = numberAt2(value, "lease_ms");
1710
- const observed = textAt(value, "observed_at");
1711
- const session = sessionAt(value);
1712
- if (sequence === void 0 || sequence < 1) return void 0;
1713
- if (lease === void 0 || observed === void 0 || session === void 0)
1714
- return void 0;
1715
- const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
1716
- if (marked && marker === void 0) return void 0;
1717
- return {
1718
- sequence,
1719
- session,
1720
- lease_ms: lease,
1721
- observed_at: observed,
1722
- ...marker !== void 0 ? { launch_identity: marker } : {}
1723
- };
1724
- }
1725
1662
  function createChannelWriter(input) {
1726
1663
  const { store, client, scheduler, recordName, channel, publisher } = input;
1727
1664
  const codec = input.codec ?? executionCodec;
@@ -1737,7 +1674,7 @@ function createChannelWriter(input) {
1737
1674
  function blank(agentId) {
1738
1675
  return {
1739
1676
  version: RADAR_PUBLICATION_VERSION,
1740
- agent_id: agentId,
1677
+ registration_id: agentId,
1741
1678
  channel,
1742
1679
  publisher,
1743
1680
  sequence: 0
@@ -1745,7 +1682,7 @@ function createChannelWriter(input) {
1745
1682
  }
1746
1683
  function loadRecord() {
1747
1684
  const value = store.read(recordName);
1748
- const agentId = textAt(value, "agent_id");
1685
+ const agentId = textAt(value, "registration_id");
1749
1686
  const storedPublisher = recordAt2(value, "publisher");
1750
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)
1751
1688
  return blank("");
@@ -1760,7 +1697,7 @@ function createChannelWriter(input) {
1760
1697
  const sentAt = numberAt2(acceptedValue, "sent_at");
1761
1698
  return {
1762
1699
  version: RADAR_PUBLICATION_VERSION,
1763
- agent_id: agentId,
1700
+ registration_id: agentId,
1764
1701
  channel,
1765
1702
  publisher,
1766
1703
  ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
@@ -1801,12 +1738,12 @@ function createChannelWriter(input) {
1801
1738
  if (record.writer !== void 0) return true;
1802
1739
  const agentId = await input.subject(recovering);
1803
1740
  if (agentId === void 0) return false;
1804
- if (record.agent_id !== agentId) {
1741
+ if (record.registration_id !== agentId) {
1805
1742
  record = blank(agentId);
1806
1743
  save();
1807
1744
  }
1808
1745
  const answer = await client.acquire({
1809
- agent_id: agentId,
1746
+ registration_id: agentId,
1810
1747
  channel,
1811
1748
  publisher
1812
1749
  });
@@ -1836,7 +1773,7 @@ function createChannelWriter(input) {
1836
1773
  const writer = record.writer;
1837
1774
  if (writer === void 0) return;
1838
1775
  const answer = await client.publish({
1839
- agent_id: record.agent_id,
1776
+ registration_id: record.registration_id,
1840
1777
  channel,
1841
1778
  writer_handle: writer.handle,
1842
1779
  sequence: pending.sequence,
@@ -1930,9 +1867,9 @@ function createChannelWriter(input) {
1930
1867
  stopPump();
1931
1868
  const writer = record.writer;
1932
1869
  if (writer === void 0) return;
1933
- const agentId = record.agent_id;
1870
+ const agentId = record.registration_id;
1934
1871
  try {
1935
- 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) => {
1936
1873
  if (answer.ok === true) {
1937
1874
  try {
1938
1875
  record.retired_at = new Date(scheduler.now()).toISOString();
@@ -1955,214 +1892,127 @@ function createChannelWriter(input) {
1955
1892
  diagnostic: () => record.diagnostic
1956
1893
  };
1957
1894
  }
1958
- function createContextWriter(input) {
1959
- const { store, client, scheduler, recordName, publisher } = input;
1960
- let record = loadRecord();
1961
- let desired;
1962
- let desiredMarker;
1963
- let running = false;
1964
- let again = false;
1965
- let recovering = false;
1966
- let stopped = false;
1967
- let fenced = false;
1968
- let pump;
1969
- function blank(agentId) {
1970
- return {
1971
- version: RADAR_PUBLICATION_VERSION,
1972
- agent_id: agentId,
1973
- publisher,
1974
- sequence: 0
1975
- };
1976
- }
1977
- function loadRecord() {
1978
- const value = store.read(recordName);
1979
- const agentId = textAt(value, "agent_id");
1980
- const storedPublisher = recordAt2(value, "publisher");
1981
- 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)
1982
- return blank("");
1983
- const writer = recordAt2(value, "writer");
1984
- const handle = textAt(writer, "handle");
1985
- const generation = numberAt2(writer, "generation");
1986
- if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
1987
- return blank(agentId);
1988
- const pending = contextReportAt(
1989
- recordAt2(value, "pending"),
1990
- CONTEXT_PENDING_KEYS,
1991
- CONTEXT_PENDING_MARKED_KEYS
1992
- );
1993
- const acceptedValue = recordAt2(value, "accepted");
1994
- const accepted = contextReportAt(
1995
- acceptedValue,
1996
- CONTEXT_ACCEPTED_KEYS,
1997
- CONTEXT_ACCEPTED_MARKED_KEYS
1998
- );
1999
- const sentAt = numberAt2(acceptedValue, "sent_at");
2000
- return {
2001
- version: RADAR_PUBLICATION_VERSION,
2002
- agent_id: agentId,
2003
- publisher,
2004
- ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
2005
- sequence: numberAt2(value, "sequence") ?? 0,
2006
- ...pending !== void 0 ? { pending } : {},
2007
- ...accepted !== void 0 && sentAt !== void 0 ? { accepted: { ...accepted, sent_at: sentAt } } : {},
2008
- ...textAt(value, "diagnostic") !== void 0 ? { diagnostic: textAt(value, "diagnostic") } : {}
2009
- };
2010
- }
2011
- function note(message) {
2012
- if (record.diagnostic !== void 0) return;
2013
- record.diagnostic = message.slice(0, 256);
2014
- 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)}`;
2015
1906
  }
1907
+ let timer;
1908
+ let stopped = false;
1909
+ let running;
2016
1910
  function save() {
2017
- store.write(recordName, record);
2018
- }
2019
- function fence(message) {
2020
- fenced = true;
2021
- stopPump();
2022
- note(`context stopped: ${message}`);
2023
- }
2024
- function startPump() {
2025
- if (pump !== void 0 || stopped || fenced) return;
2026
- pump = scheduler.setInterval(() => kick(), input.heartbeatMs);
2027
- pump.unref?.();
2028
- }
2029
- function stopPump() {
2030
- if (pump === void 0) return;
2031
- scheduler.clearInterval(pump);
2032
- 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
+ }
2033
1918
  }
2034
- async function ensureSubject() {
2035
- if (record.writer !== void 0) return true;
2036
- const agentId = await input.subject(recovering);
2037
- if (agentId === void 0) return false;
2038
- if (record.agent_id !== agentId) {
2039
- 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);
2040
1927
  save();
2041
1928
  }
2042
- return true;
2043
1929
  }
2044
- function heartbeatDue() {
2045
- 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?.();
2046
1937
  }
2047
- async function send(pending) {
2048
- const writer = record.writer;
2049
- const answer = await client.context({
2050
- agent_id: record.agent_id,
2051
- publisher,
2052
- // A first publish presents no handle: the daemon binds the writer the
2053
- // publisher already is, which is the binding this writer then keeps.
2054
- ...writer !== void 0 ? { writer_handle: writer.handle } : {},
2055
- sequence: pending.sequence,
2056
- lease_ms: pending.lease_ms,
2057
- observed_at: pending.observed_at,
2058
- context: {
2059
- session: pending.session,
2060
- ...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;
2061
1959
  }
2062
- });
2063
- if (answer.ok === true) {
2064
- recovering = false;
2065
- record.writer = {
2066
- handle: answer.value.writer.handle,
2067
- generation: answer.value.writer.generation
2068
- };
2069
- record.sequence = Math.max(record.sequence, answer.value.writer.sequence);
2070
- record.accepted = {
2071
- ...pending,
2072
- sent_at: answer.value.warning === void 0 ? scheduler.now() : record.accepted?.sent_at ?? scheduler.now()
2073
- };
2074
- 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;
2075
1964
  save();
2076
- return;
2077
- }
2078
- if (answer.code === "refused" || answer.code === "bad_params" || // A daemon that speaks the registry but does not serve this record would
2079
- // refuse every retry the same way; one diagnostic beats a silent retry
2080
- // loop once per heartbeat.
2081
- answer.code === "unknown_method") {
2082
- fence(answer.message);
2083
- return;
2084
- }
2085
- if (answer.code === "not_found") {
2086
- record = blank("");
2087
- recovering = true;
2088
- save();
2089
- return;
2090
- }
2091
- }
2092
- async function stage(session, launchIdentity) {
2093
- const pending = {
2094
- sequence: record.sequence + 1,
2095
- session,
2096
- lease_ms: input.leaseMs,
2097
- observed_at: new Date(scheduler.now()).toISOString(),
2098
- ...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
2099
- };
2100
- record.pending = pending;
2101
- save();
2102
- await send(pending);
2103
- }
2104
- async function drainOnce() {
2105
- if (fenced) return;
2106
- if (record.pending !== void 0) {
2107
- await send(record.pending);
2108
- return;
2109
- }
2110
- if (desired === void 0) return;
2111
- if (!await ensureSubject()) return;
2112
- if (fenced) return;
2113
- if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
2114
- await stage(desired, desiredMarker);
2115
- return;
2116
- }
2117
- if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
2118
- }
2119
- async function drain() {
2120
- if (running || stopped || fenced) return;
2121
- running = true;
1965
+ return claim.registration_id;
1966
+ })();
2122
1967
  try {
2123
- await drainOnce();
1968
+ return await running;
2124
1969
  } catch (error) {
2125
- fenced = true;
2126
- stopPump();
2127
- try {
2128
- note(`context storage failure: ${String(error).slice(0, 180)}`);
2129
- } catch {
2130
- }
1970
+ diagnostic = `presence unavailable: ${String(error)}`;
1971
+ return void 0;
2131
1972
  } finally {
2132
- running = false;
2133
- if (again && !stopped && !fenced) {
2134
- again = false;
2135
- kick();
2136
- }
1973
+ running = void 0;
1974
+ if (!stopped && state !== claim) void ensure();
1975
+ else schedule();
2137
1976
  }
2138
1977
  }
2139
- function kick() {
2140
- if (stopped || fenced) return;
2141
- startPump();
2142
- if (running) {
2143
- again = true;
2144
- return;
2145
- }
2146
- void drain().catch(() => void 0);
2147
- }
2148
1978
  return {
2149
- update(session, launchIdentity) {
2150
- if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
2151
- return;
2152
- desired = session;
2153
- desiredMarker = launchIdentity;
2154
- if (stopped) return;
2155
- 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();
2156
2004
  },
2157
2005
  stop() {
2158
2006
  stopped = true;
2159
- stopPump();
2007
+ if (timer) scheduler.clearTimeout(timer);
2008
+ timer = void 0;
2160
2009
  },
2161
2010
  reopen() {
2162
2011
  stopped = false;
2163
- if (desired !== void 0 || record.pending !== void 0) kick();
2012
+ void ensure();
2164
2013
  },
2165
- diagnostic: () => record.diagnostic
2014
+ management: () => standing,
2015
+ diagnostic: () => diagnostic
2166
2016
  };
2167
2017
  }
2168
2018
  var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
@@ -2191,10 +2041,13 @@ function unavailablePublication(error) {
2191
2041
  };
2192
2042
  return {
2193
2043
  execution: writer,
2194
- context: {
2195
- update: () => void 0,
2044
+ presence: {
2045
+ report: () => void 0,
2046
+ ensure: async () => void 0,
2047
+ release: async () => void 0,
2196
2048
  stop: () => void 0,
2197
2049
  reopen: () => void 0,
2050
+ management: () => void 0,
2198
2051
  diagnostic: () => diagnostic
2199
2052
  },
2200
2053
  facts: { ...writer, update: () => void 0 },
@@ -2221,33 +2074,29 @@ function createRadarPublicationInner(options) {
2221
2074
  cached.reopen();
2222
2075
  return cached;
2223
2076
  }
2224
- 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`;
2225
2078
  const channelRecord = (channel, target) => `channels/${createHash("sha256").update(`${channel}\0${incarnation}\0${target ?? ""}`).digest("hex").slice(0, 32)}.json`;
2226
- const contextRecord = `context/${createHash("sha256").update(`context\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2227
- function readSubject() {
2228
- 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);
2229
2082
  const registration = recordAt2(value, "registration");
2230
2083
  if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || textAt(registration, "source") !== options.registration.source || textAt(registration, "incarnation") !== incarnation)
2231
2084
  return void 0;
2232
- const storedAgentId = textAt(value, "agent_id");
2085
+ const storedAgentId = textAt(value, "registration_id");
2233
2086
  return {
2234
2087
  version: RADAR_PUBLICATION_VERSION,
2235
2088
  registration,
2236
- ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { agent_id: storedAgentId } : {}
2089
+ ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { registration_id: storedAgentId } : {}
2237
2090
  };
2238
2091
  }
2239
- const stored = readSubject();
2240
- let subject = stored;
2092
+ const stored = readRegistration();
2093
+ let processRecord = stored;
2241
2094
  let registrationDiagnostic;
2242
2095
  let registrationRecord;
2243
2096
  async function ensureRegistration() {
2244
- if (subject !== void 0) return subject;
2097
+ if (processRecord !== void 0) return processRecord;
2245
2098
  registrationRecord ??= (async () => {
2246
2099
  const resolution = options.location ? await options.location() : { state: "absent", reason: "this process publishes no location" };
2247
- if (resolution.state === "pending") {
2248
- registrationDiagnostic = `location bootstrap pending: ${resolution.reason}`.slice(0, 256);
2249
- return void 0;
2250
- }
2251
2100
  const built = {
2252
2101
  version: RADAR_PUBLICATION_VERSION,
2253
2102
  registration: {
@@ -2258,13 +2107,13 @@ function createRadarPublicationInner(options) {
2258
2107
  }
2259
2108
  };
2260
2109
  try {
2261
- store.write(subjectName, built);
2110
+ store.write(registrationName, built);
2262
2111
  } catch (error) {
2263
2112
  registrationDiagnostic ??= `private storage unavailable: ${String(error)}`.slice(0, 256);
2264
2113
  return void 0;
2265
2114
  }
2266
2115
  registrationDiagnostic = void 0;
2267
- subject = built;
2116
+ processRecord = built;
2268
2117
  return built;
2269
2118
  })();
2270
2119
  try {
@@ -2273,11 +2122,10 @@ function createRadarPublicationInner(options) {
2273
2122
  registrationRecord = void 0;
2274
2123
  }
2275
2124
  }
2276
- let agentId = stored?.agent_id;
2125
+ let agentId = stored?.registration_id;
2277
2126
  let subjectReady;
2278
2127
  async function ensureSubject(recheck) {
2279
- if (recheck) agentId = void 0;
2280
- if (agentId !== void 0) return agentId;
2128
+ if (!recheck && agentId !== void 0) return agentId;
2281
2129
  subjectReady ??= (async () => {
2282
2130
  const ready = await client.ping();
2283
2131
  if (ready.ok === false) {
@@ -2287,18 +2135,11 @@ function createRadarPublicationInner(options) {
2287
2135
  }
2288
2136
  const record = await ensureRegistration();
2289
2137
  if (record === void 0) return void 0;
2290
- const answer = await client.register({
2291
- ...record.registration,
2292
- ...spawnToken !== void 0 ? { spawn_token: spawnToken } : {}
2293
- });
2294
- if (answer.ok === false) {
2295
- if (answer.code !== "absent" && answer.code !== "timeout" && answer.code !== "transport")
2296
- registrationDiagnostic ??= `subject registration refused: ${answer.message}`.slice(0, 256);
2297
- return void 0;
2298
- }
2299
- agentId = answer.value.agent_id;
2300
- subject = { ...record, agent_id: agentId };
2301
- 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);
2302
2143
  if (options.binding !== void 0) writeBinding(options.binding);
2303
2144
  return agentId;
2304
2145
  })();
@@ -2312,7 +2153,7 @@ function createRadarPublicationInner(options) {
2312
2153
  if (agentId === void 0) return;
2313
2154
  store.write(`bindings/${binding.key}.json`, {
2314
2155
  version: RADAR_PUBLICATION_VERSION,
2315
- agent_id: agentId,
2156
+ registration_id: agentId,
2316
2157
  incarnation,
2317
2158
  run: binding.run,
2318
2159
  owner: binding.owner,
@@ -2336,14 +2177,22 @@ function createRadarPublicationInner(options) {
2336
2177
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2337
2178
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2338
2179
  });
2339
- const context = createContextWriter({
2180
+ const presence = createPresenceReporter({
2340
2181
  store,
2341
2182
  client,
2342
2183
  scheduler,
2343
- recordName: contextRecord,
2344
- publisher: publisher("herdsman-pi"),
2345
- subject: ensureSubject,
2346
- 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
+ },
2347
2196
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2348
2197
  });
2349
2198
  const facts = createChannelWriter({
@@ -2361,7 +2210,7 @@ function createRadarPublicationInner(options) {
2361
2210
  const assignmentWriters = /* @__PURE__ */ new Map();
2362
2211
  const publication = {
2363
2212
  execution,
2364
- context,
2213
+ presence,
2365
2214
  facts,
2366
2215
  assignment(target, reportingOwner) {
2367
2216
  const existing = assignmentWriters.get(target);
@@ -2376,7 +2225,7 @@ function createRadarPublicationInner(options) {
2376
2225
  publisher: publisher("herdsman-owner", reportingOwner),
2377
2226
  codec: assignmentCodec,
2378
2227
  // The owner publishes about a subject it did not register: the exact
2379
- // agent_id from the child's sidecar is the whole correlation.
2228
+ // registration_id from the child's sidecar is the whole correlation.
2380
2229
  subject: async () => target,
2381
2230
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2382
2231
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
@@ -2396,7 +2245,7 @@ function createRadarPublicationInner(options) {
2396
2245
  registrationDiagnostic ??= `Radar binding unavailable: ${String(error).slice(0, 180)}`;
2397
2246
  return void 0;
2398
2247
  }
2399
- const bound = textAt(value, "agent_id");
2248
+ const bound = textAt(value, "registration_id");
2400
2249
  const boundIncarnation = textAt(value, "incarnation");
2401
2250
  const run = textAt(value, "run");
2402
2251
  const owner = textAt(value, "owner");
@@ -2406,7 +2255,7 @@ function createRadarPublicationInner(options) {
2406
2255
  return void 0;
2407
2256
  return {
2408
2257
  version: RADAR_PUBLICATION_VERSION,
2409
- agent_id: bound,
2258
+ registration_id: bound,
2410
2259
  incarnation: boundIncarnation,
2411
2260
  run,
2412
2261
  owner,
@@ -2422,25 +2271,27 @@ function createRadarPublicationInner(options) {
2422
2271
  if (agentId2 === void 0) return void 0;
2423
2272
  const record = await ensureRegistration();
2424
2273
  if (record === void 0) return void 0;
2425
- const location = record.registration.location;
2426
- const subjectId = process.env.PI_RADAR_MANAGED_SUBJECT_ID;
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;
2427
2278
  return {
2428
- agent_id: agentId2,
2279
+ registration_id: agentId2,
2429
2280
  ...subjectId !== void 0 ? { subject_id: subjectId } : {},
2430
2281
  ...location !== void 0 ? { location } : {}
2431
2282
  };
2432
2283
  },
2433
- diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? context.diagnostic() ?? facts.diagnostic(),
2284
+ diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? presence.diagnostic() ?? facts.diagnostic(),
2434
2285
  stop() {
2435
2286
  for (const writer of assignmentWriters.values()) writer.stop();
2436
2287
  execution.stop();
2437
- context.stop();
2288
+ presence.stop();
2438
2289
  facts.stop();
2439
2290
  },
2440
2291
  reopen() {
2441
2292
  for (const writer of assignmentWriters.values()) writer.reopen();
2442
2293
  execution.reopen();
2443
- context.reopen();
2294
+ presence.reopen();
2444
2295
  facts.reopen();
2445
2296
  }
2446
2297
  };
@@ -2478,10 +2329,13 @@ function unavailablePublication2() {
2478
2329
  return {
2479
2330
  execution: writer,
2480
2331
  facts: { ...writer, update: () => void 0 },
2481
- context: {
2482
- update: () => void 0,
2332
+ presence: {
2333
+ report: () => void 0,
2483
2334
  stop: () => void 0,
2484
2335
  reopen: () => void 0,
2336
+ management: () => void 0,
2337
+ ensure: async () => void 0,
2338
+ release: async () => void 0,
2485
2339
  diagnostic: () => "Radar private storage is unavailable"
2486
2340
  },
2487
2341
  assignment: () => writer,
@@ -2564,11 +2418,11 @@ function createRadarExecutionAdapter(pi, options) {
2564
2418
  ...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
2565
2419
  };
2566
2420
  }
2567
- function reportSession(context, launchIdentity) {
2421
+ function reportSession(context) {
2568
2422
  const session = context?.sessionManager?.getSessionId?.();
2569
2423
  if (!isUuid(session)) return;
2570
2424
  try {
2571
- publication.context.update(session, launchIdentity);
2425
+ publication.presence.report(session);
2572
2426
  } catch {
2573
2427
  }
2574
2428
  }
@@ -2632,38 +2486,8 @@ function createRadarExecutionAdapter(pi, options) {
2632
2486
  })
2633
2487
  );
2634
2488
  on("session_start", (_event, context) => {
2635
- const sessionHost = {
2636
- appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
2637
- sessionManager: context?.sessionManager
2638
- };
2639
- let launchIdentity;
2640
- try {
2641
- const result = persistRadarLaunchMarker(
2642
- sessionHost,
2643
- options.markerEnvironment
2644
- );
2645
- if (result === "written" || result === "unchanged") {
2646
- const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
2647
- const activeMarker = activeRadarLaunchMarker(sessionHost);
2648
- if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
2649
- launchIdentity = activeMarker.marker;
2650
- } else {
2651
- options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
2652
- }
2653
- } else if (result === "unsupported") {
2654
- options.diagnostic?.("Pi host does not support Radar launch identity session entries");
2655
- } else if (options.markerEnvironment && [
2656
- "PI_RADAR_MANAGED_SUBJECT_ID",
2657
- "PI_RADAR_MANAGED_LAUNCH_ID",
2658
- "PI_RADAR_MANAGED_EXECUTION_GENERATION"
2659
- ].some((key) => options.markerEnvironment?.[key] !== void 0)) {
2660
- options.diagnostic?.("incomplete or invalid Radar launch identity environment");
2661
- }
2662
- } catch (error) {
2663
- options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
2664
- }
2665
2489
  rootSession = true;
2666
- reportSession(context, launchIdentity);
2490
+ reportSession(context);
2667
2491
  publishFacts(context);
2668
2492
  publication.reopen();
2669
2493
  active = context?.isIdle?.() === false;
@@ -2694,8 +2518,9 @@ function createRadarExecutionAdapter(pi, options) {
2694
2518
  }
2695
2519
  emit();
2696
2520
  });
2697
- on("session_shutdown", () => {
2521
+ on("session_shutdown", async () => {
2698
2522
  suspend();
2523
+ await publication.presence.release();
2699
2524
  });
2700
2525
  function suspend() {
2701
2526
  if (stopped) return;
@@ -2903,7 +2728,7 @@ function trajectory(reason) {
2903
2728
  return `the daemon's current record of this launch's child is ${reason}`;
2904
2729
  }
2905
2730
  async function observeRadarChild(client, provenance) {
2906
- if (provenance.agent_id === void 0)
2731
+ if (provenance.registration_id === void 0)
2907
2732
  return unknownChild("not bound to a child by this launch");
2908
2733
  if (provenance.pane_id === void 0)
2909
2734
  return unknownChild("without a pane this launch recorded for it");
@@ -2914,11 +2739,11 @@ async function observeRadarChild(client, provenance) {
2914
2739
  );
2915
2740
  const topology = read.value;
2916
2741
  const edge = topology.edge;
2917
- if (edge.bound === void 0 || edge.bound.agent_id === void 0)
2742
+ if (edge.bound === void 0 || edge.bound.registration_id === void 0)
2918
2743
  return unknownChild("not bound to a child by the daemon");
2919
- if (edge.bound.agent_id !== provenance.agent_id)
2744
+ if (edge.bound.registration_id !== provenance.registration_id)
2920
2745
  return unknownChild(
2921
- `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}`
2922
2747
  );
2923
2748
  if (edge.location?.pane === void 0)
2924
2749
  return unknownChild("recorded without a pane");
@@ -2936,7 +2761,7 @@ async function observeRadarChild(client, provenance) {
2936
2761
  return unknownChild(
2937
2762
  `not currently placed (state ${topology.state ?? "unreported"})`
2938
2763
  );
2939
- const reply = await client.agentGet(edge.bound.agent_id);
2764
+ const reply = await client.agentGet(edge.bound.registration_id);
2940
2765
  if (reply.ok === false)
2941
2766
  return unknownChild(
2942
2767
  `process verdict not readable (${reply.code}: ${reply.message})`
@@ -2964,7 +2789,7 @@ async function observeRadarChild(client, provenance) {
2964
2789
  kind: "live",
2965
2790
  evidence: {
2966
2791
  request_id: provenance.request_id,
2967
- agent_id: edge.bound.agent_id,
2792
+ registration_id: edge.bound.registration_id,
2968
2793
  source: edge.bound.source,
2969
2794
  incarnation: edge.bound.incarnation,
2970
2795
  pane_id: edge.location.pane
@@ -3024,7 +2849,7 @@ async function launchRadarWorker(options) {
3024
2849
  const deadline = Date.now() + options.timeoutMs;
3025
2850
  const remaining = () => Math.max(0, deadline - Date.now());
3026
2851
  const pane = launch.parent.location?.pane;
3027
- if (!isUuid(launch.parent.agent_id))
2852
+ if (!isUuid(launch.parent.registration_id))
3028
2853
  return refusedLaunch(
3029
2854
  "the launch has no exact parent registration to spawn under"
3030
2855
  );
@@ -3038,7 +2863,7 @@ async function launchRadarWorker(options) {
3038
2863
  );
3039
2864
  const spawned = await client.spawn({
3040
2865
  request_id: launch.request_id,
3041
- parent: launch.parent.agent_id,
2866
+ parent: launch.parent.registration_id,
3042
2867
  parent_session_id: launch.parent_session_id,
3043
2868
  executable: launch.executable,
3044
2869
  managed: { parent_subject_id: launch.parent.subject_id },
@@ -3063,10 +2888,10 @@ async function launchRadarWorker(options) {
3063
2888
  ...signal !== void 0 ? { signal } : {}
3064
2889
  });
3065
2890
  if (bound.kind !== "bound") return bound;
3066
- const agentId = bound.agent_id;
2891
+ const agentId = bound.registration_id;
3067
2892
  if (launch.onBound !== void 0) {
3068
2893
  try {
3069
- launch.onBound({ agent_id: agentId, pane_id: bound.pane_id });
2894
+ launch.onBound({ registration_id: agentId, pane_id: bound.pane_id });
3070
2895
  } catch (error) {
3071
2896
  return unresolvedLaunch(
3072
2897
  launch.request_id,
@@ -3094,7 +2919,7 @@ async function launchRadarWorker(options) {
3094
2919
  kind: "ready",
3095
2920
  value: {
3096
2921
  request_id: launch.request_id,
3097
- agent_id: agentId,
2922
+ registration_id: agentId,
3098
2923
  pane_id: bound.pane_id,
3099
2924
  bound_process: {
3100
2925
  source: bound.source,
@@ -3140,7 +2965,7 @@ async function waitForChild(client, requestId, options) {
3140
2965
  requestId,
3141
2966
  "the daemon observed the launched child's process exit"
3142
2967
  );
3143
- const agentId = edge.bound?.agent_id;
2968
+ const agentId = edge.bound?.registration_id;
3144
2969
  if (agentId !== void 0 && isUuid(agentId)) {
3145
2970
  const source = edge.bound?.source;
3146
2971
  const incarnation = edge.bound?.incarnation;
@@ -3151,7 +2976,7 @@ async function waitForChild(client, requestId, options) {
3151
2976
  );
3152
2977
  return {
3153
2978
  kind: "bound",
3154
- agent_id: agentId,
2979
+ registration_id: agentId,
3155
2980
  pane_id: pane,
3156
2981
  source,
3157
2982
  incarnation