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
@@ -113,6 +113,25 @@ function recordAt(value, field) {
113
113
  function isUuid(value) {
114
114
  return typeof value === "string" && UUID.test(value);
115
115
  }
116
+ function decodePresenceAnswer(value) {
117
+ const registrationId = recordAt(value, "registration_id");
118
+ const claimId = recordAt(value, "claim_id");
119
+ const standing = recordAt(value, "standing");
120
+ const subjectId = recordAt(value, "subject_id");
121
+ if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
122
+ return protocol("presence reply carries no valid claim standing");
123
+ return { ok: true, value };
124
+ }
125
+ function managementAt(value) {
126
+ const field = recordAt(value, "management");
127
+ const state = stringAt(field, "state");
128
+ if (state === "managed") {
129
+ const subjectId = stringAt(field, "subject_id");
130
+ return subjectId === void 0 ? void 0 : { state, subject_id: subjectId };
131
+ }
132
+ if (state === "duplicate" || state === "pending" || state === "unmanaged") return { state };
133
+ return void 0;
134
+ }
116
135
  function stringAt(value, field) {
117
136
  if (typeof value !== "object" || value === null) return void 0;
118
137
  const found = value[field];
@@ -133,7 +152,7 @@ function handoffAt(value) {
133
152
  const content = stringAt(value, "content");
134
153
  const published = numberAt(value, "published_at_unix_ms");
135
154
  const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
136
- 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;
155
+ 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;
137
156
  return value;
138
157
  }
139
158
  function handoffReceiptAt(value) {
@@ -142,7 +161,7 @@ function handoffReceiptAt(value) {
142
161
  const recipient = recordAt(value, "recipient");
143
162
  const generation = numberAt(recipient, "execution_generation");
144
163
  const received = numberAt(value, "received_at_unix_ms");
145
- 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;
164
+ 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;
146
165
  return value;
147
166
  }
148
167
  function writerBinding(value) {
@@ -296,7 +315,7 @@ function spawnEdge(value) {
296
315
  const bound = recordAt(value, "bound");
297
316
  const source = stringAt(bound, "source");
298
317
  const incarnation = stringAt(bound, "incarnation");
299
- const agentId = stringAt(bound, "agent_id");
318
+ const agentId = stringAt(bound, "registration_id");
300
319
  const exit = exitAt(recordAt(value, "exit_observation"));
301
320
  return {
302
321
  ok: true,
@@ -310,7 +329,7 @@ function spawnEdge(value) {
310
329
  bound: {
311
330
  source,
312
331
  incarnation,
313
- ...isUuid(agentId) ? { agent_id: agentId } : {}
332
+ ...isUuid(agentId) ? { registration_id: agentId } : {}
314
333
  }
315
334
  } : {},
316
335
  ...exit !== void 0 ? { exit_observation: exit } : {}
@@ -650,6 +669,22 @@ function createRadarClient(options = {}) {
650
669
  return {
651
670
  socketPath,
652
671
  ping,
672
+ async presence(request) {
673
+ return withRegistry(async () => {
674
+ const answer = await call("agent.presence", { ...request });
675
+ if (!answer.ok) return answer;
676
+ return decodePresenceAnswer(answer.value);
677
+ });
678
+ },
679
+ async releasePresence(request) {
680
+ return withRegistry(async () => {
681
+ const answer = await call("agent.release", { ...request });
682
+ if (!answer.ok) return answer;
683
+ const decoded = decodePresenceAnswer(answer.value);
684
+ if (!decoded.ok) return decoded;
685
+ return decoded.value.standing === "released" ? decoded : protocol("release reply did not confirm the claim release");
686
+ });
687
+ },
653
688
  async register(registration) {
654
689
  return withRegistry(async () => {
655
690
  const answer = await call("agent.register", {
@@ -665,15 +700,15 @@ function createRadarClient(options = {}) {
665
700
  });
666
701
  if (answer.ok === false) return answer;
667
702
  const record = answer.value.registration;
668
- const agentId = stringAt(record, "agent_id");
669
- if (!isUuid(agentId)) return protocol("registration reply carries no agent_id");
670
- return { ok: true, value: { agent_id: agentId } };
703
+ const agentId = stringAt(record, "registration_id");
704
+ if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
705
+ return { ok: true, value: { registration_id: agentId } };
671
706
  });
672
707
  },
673
708
  async acquire(request) {
674
709
  return withRegistryChannel(request.channel, async () => {
675
710
  const answer = await call("agent.acquire", {
676
- agent_id: request.agent_id,
711
+ registration_id: request.registration_id,
677
712
  channel: request.channel,
678
713
  publisher: request.publisher,
679
714
  ...request.replace !== void 0 ? { replace: request.replace } : {}
@@ -702,7 +737,7 @@ function createRadarClient(options = {}) {
702
737
  }
703
738
  return withRegistryChannel(request.channel, async () => {
704
739
  const answer = await call("agent.publish", {
705
- agent_id: request.agent_id,
740
+ registration_id: request.registration_id,
706
741
  channel: request.channel,
707
742
  writer_handle: request.writer_handle,
708
743
  sequence: request.sequence,
@@ -721,7 +756,7 @@ function createRadarClient(options = {}) {
721
756
  async context(request) {
722
757
  return withRegistry(async () => {
723
758
  const answer = await call("agent.context", {
724
- agent_id: request.agent_id,
759
+ registration_id: request.registration_id,
725
760
  publisher: request.publisher,
726
761
  ...request.writer_handle !== void 0 ? { writer_handle: request.writer_handle } : {},
727
762
  ...request.replace !== void 0 ? { replace: request.replace } : {},
@@ -737,9 +772,14 @@ function createRadarClient(options = {}) {
737
772
  if (writer === void 0)
738
773
  return protocol("context reply carries no writer binding");
739
774
  const warning = stringAt(answer.value, "warning");
775
+ const management = managementAt(answer.value);
740
776
  return {
741
777
  ok: true,
742
- value: { writer, ...warning !== void 0 ? { warning } : {} }
778
+ value: {
779
+ writer,
780
+ ...warning !== void 0 ? { warning } : {},
781
+ ...management !== void 0 ? { management } : {}
782
+ }
743
783
  };
744
784
  });
745
785
  },
@@ -816,7 +856,7 @@ function createRadarClient(options = {}) {
816
856
  message: "agent.get requires a canonical agent UUID"
817
857
  };
818
858
  return withRegistry(async () => {
819
- const answer = await call("agent.get", { agent_id: agentId });
859
+ const answer = await call("agent.get", { registration_id: agentId });
820
860
  if (answer.ok === false) return answer;
821
861
  return agentProcessAt(recordAt(answer.value, "agent"));
822
862
  });
@@ -870,7 +910,7 @@ function createRadarClient(options = {}) {
870
910
  async communicationPublish(request) {
871
911
  return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
872
912
  const answer = await call("communication.publish", {
873
- sender_agent_id: request.sender_agent_id,
913
+ sender_registration_id: request.sender_registration_id,
874
914
  recipient_subject_id: request.recipient_subject_id,
875
915
  ...request.correlation_id !== void 0 ? { correlation_id: request.correlation_id } : {},
876
916
  summary: request.summary,
@@ -1141,7 +1181,7 @@ function boundedRadarSnapshot(snapshot) {
1141
1181
  function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
1142
1182
  async function agentId() {
1143
1183
  const subject = await publication.subject();
1144
- return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.agent_id };
1184
+ return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.registration_id };
1145
1185
  }
1146
1186
  return {
1147
1187
  async publish(request) {
@@ -1152,24 +1192,24 @@ function createRadarHandoffPort(client, publication, parentSubjectId = () => pro
1152
1192
  return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
1153
1193
  return client.communicationPublish({
1154
1194
  ...request,
1155
- sender_agent_id: identity.value,
1195
+ sender_registration_id: identity.value,
1156
1196
  recipient_subject_id: parent
1157
1197
  });
1158
1198
  },
1159
1199
  async inbox() {
1160
1200
  const identity = await agentId();
1161
1201
  if (identity.ok === false) return identity;
1162
- return client.communicationInbox({ agent_id: identity.value });
1202
+ return client.communicationInbox({ registration_id: identity.value });
1163
1203
  },
1164
1204
  async get(handoff_id) {
1165
1205
  const identity = await agentId();
1166
1206
  if (identity.ok === false) return identity;
1167
- return client.communicationGet({ agent_id: identity.value, handoff_id });
1207
+ return client.communicationGet({ registration_id: identity.value, handoff_id });
1168
1208
  },
1169
1209
  async ack(handoff_id) {
1170
1210
  const identity = await agentId();
1171
1211
  if (identity.ok === false) return identity;
1172
- return client.communicationAck({ agent_id: identity.value, handoff_id });
1212
+ return client.communicationAck({ registration_id: identity.value, handoff_id });
1173
1213
  }
1174
1214
  };
1175
1215
  }
@@ -1190,77 +1230,7 @@ import {
1190
1230
  writeSync
1191
1231
  } from "node:fs";
1192
1232
  import { dirname as dirname2, join as join2, parse, resolve, sep } from "node:path";
1193
-
1194
- // extension/radar-session-marker.ts
1195
- var RADAR_LAUNCH_MARKER_TYPE = "pi-radar.launch-identity";
1196
- var RADAR_LAUNCH_MARKER_VERSION = 1;
1197
- var RADAR_MAX_EXECUTION_GENERATION = 4294967295;
1198
- var RADAR_MANAGED_SUBJECT_ENV = "PI_RADAR_MANAGED_SUBJECT_ID";
1199
- var RADAR_MANAGED_LAUNCH_ENV = "PI_RADAR_MANAGED_LAUNCH_ID";
1200
- var RADAR_MANAGED_GENERATION_ENV = "PI_RADAR_MANAGED_EXECUTION_GENERATION";
1201
- 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;
1202
- function decodeRadarLaunchMarker(value) {
1203
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1204
- const record = value;
1205
- if (Object.keys(record).sort().join(",") !== "execution_generation,launch_id,subject_id,version") {
1206
- return void 0;
1207
- }
1208
- 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) {
1209
- return void 0;
1210
- }
1211
- return {
1212
- version: RADAR_LAUNCH_MARKER_VERSION,
1213
- subject_id: record.subject_id,
1214
- launch_id: record.launch_id,
1215
- execution_generation: record.execution_generation
1216
- };
1217
- }
1218
- function appendRadarLaunchMarker(appendEntry, marker) {
1219
- const validated = decodeRadarLaunchMarker(marker);
1220
- if (!validated) throw new Error("invalid Radar launch identity marker");
1221
- appendEntry(RADAR_LAUNCH_MARKER_TYPE, validated);
1222
- }
1223
- function latestRadarLaunchMarker(branch) {
1224
- let latest;
1225
- for (const entry of branch) {
1226
- if (entry.type === "custom" && entry.customType === RADAR_LAUNCH_MARKER_TYPE) {
1227
- latest = entry;
1228
- }
1229
- }
1230
- if (!latest) return { status: "absent" };
1231
- const marker = decodeRadarLaunchMarker(latest.data);
1232
- return marker ? { status: "valid", marker } : { status: "invalid" };
1233
- }
1234
- function radarLaunchMarkerFromEnvironment(env = process.env) {
1235
- const rawGeneration = env[RADAR_MANAGED_GENERATION_ENV];
1236
- if (env[RADAR_MANAGED_SUBJECT_ENV] === void 0 || env[RADAR_MANAGED_LAUNCH_ENV] === void 0 || rawGeneration === void 0 || !/^[1-9][0-9]*$/.test(rawGeneration)) {
1237
- return void 0;
1238
- }
1239
- return decodeRadarLaunchMarker({
1240
- version: RADAR_LAUNCH_MARKER_VERSION,
1241
- subject_id: env[RADAR_MANAGED_SUBJECT_ENV],
1242
- launch_id: env[RADAR_MANAGED_LAUNCH_ENV],
1243
- execution_generation: Number(rawGeneration)
1244
- });
1245
- }
1246
- function activeRadarLaunchMarker(host) {
1247
- if (!host.sessionManager) return { status: "absent" };
1248
- return latestRadarLaunchMarker(host.sessionManager.getBranch());
1249
- }
1250
- function persistRadarLaunchMarker(host, env = process.env) {
1251
- const marker = radarLaunchMarkerFromEnvironment(env);
1252
- if (!marker) return "absent";
1253
- if (!host.appendEntry || !host.sessionManager) return "unsupported";
1254
- const current = latestRadarLaunchMarker(host.sessionManager.getBranch());
1255
- if (current.status === "valid" && sameMarker(current.marker, marker)) return "unchanged";
1256
- appendRadarLaunchMarker(host.appendEntry, marker);
1257
- return "written";
1258
- }
1259
- function sameMarker(a, b) {
1260
- return a.version === b.version && a.subject_id === b.subject_id && a.launch_id === b.launch_id && a.execution_generation === b.execution_generation;
1261
- }
1262
-
1263
- // extension/radar-publication.ts
1233
+ var DUPLICATE_SESSION_DIAGNOSTIC = "Radar rejected this session claim as a duplicate; reload or resume the session before retrying";
1264
1234
  var RADAR_PUBLICATION_VERSION = 1;
1265
1235
  var RADAR_LEASE_MS = 3e4;
1266
1236
  var RADAR_HEARTBEAT_MS = 15e3;
@@ -1285,7 +1255,7 @@ function readProcessBirth() {
1285
1255
  return void 0;
1286
1256
  }
1287
1257
  }
1288
- var RECORD_NAME = /^(?:subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
1258
+ var RECORD_NAME = /^(?:registrations|presence|subjects|channels|context|bindings)\/[a-z0-9-]{1,64}\.json$/;
1289
1259
  var MAX_RECORD_BYTES = 64 * 1024;
1290
1260
  var PRIVATE_MODE = 448;
1291
1261
  var RECORD_MODE = 384;
@@ -1567,39 +1537,6 @@ function pendingAt(value, keys = PENDING_KEYS, codec = executionCodec) {
1567
1537
  return void 0;
1568
1538
  return { sequence, snapshot, lease_ms: lease, observed_at: observed };
1569
1539
  }
1570
- var CONTEXT_PENDING_KEYS = "lease_ms,observed_at,sequence,session";
1571
- var CONTEXT_PENDING_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sequence,session";
1572
- var CONTEXT_ACCEPTED_KEYS = "lease_ms,observed_at,sent_at,sequence,session";
1573
- var CONTEXT_ACCEPTED_MARKED_KEYS = "launch_identity,lease_ms,observed_at,sent_at,sequence,session";
1574
- function sessionAt(value) {
1575
- if (typeof value !== "object" || value === null) return void 0;
1576
- const found = value.session;
1577
- if (found === null) return null;
1578
- return typeof found === "string" ? found : void 0;
1579
- }
1580
- function sameLaunchIdentity(a, b) {
1581
- 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;
1582
- }
1583
- function contextReportAt(value, keys, markedKeys) {
1584
- const marked = exactKeys(value, markedKeys);
1585
- if (!exactKeys(value, keys) && !marked) return void 0;
1586
- const sequence = numberAt2(value, "sequence");
1587
- const lease = numberAt2(value, "lease_ms");
1588
- const observed = textAt(value, "observed_at");
1589
- const session = sessionAt(value);
1590
- if (sequence === void 0 || sequence < 1) return void 0;
1591
- if (lease === void 0 || observed === void 0 || session === void 0)
1592
- return void 0;
1593
- const marker = marked ? decodeRadarLaunchMarker(value.launch_identity) : void 0;
1594
- if (marked && marker === void 0) return void 0;
1595
- return {
1596
- sequence,
1597
- session,
1598
- lease_ms: lease,
1599
- observed_at: observed,
1600
- ...marker !== void 0 ? { launch_identity: marker } : {}
1601
- };
1602
- }
1603
1540
  function createChannelWriter(input) {
1604
1541
  const { store, client, scheduler, recordName, channel, publisher } = input;
1605
1542
  const codec = input.codec ?? executionCodec;
@@ -1615,7 +1552,7 @@ function createChannelWriter(input) {
1615
1552
  function blank(agentId) {
1616
1553
  return {
1617
1554
  version: RADAR_PUBLICATION_VERSION,
1618
- agent_id: agentId,
1555
+ registration_id: agentId,
1619
1556
  channel,
1620
1557
  publisher,
1621
1558
  sequence: 0
@@ -1623,7 +1560,7 @@ function createChannelWriter(input) {
1623
1560
  }
1624
1561
  function loadRecord() {
1625
1562
  const value = store.read(recordName);
1626
- const agentId = textAt(value, "agent_id");
1563
+ const agentId = textAt(value, "registration_id");
1627
1564
  const storedPublisher = recordAt2(value, "publisher");
1628
1565
  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)
1629
1566
  return blank("");
@@ -1638,7 +1575,7 @@ function createChannelWriter(input) {
1638
1575
  const sentAt = numberAt2(acceptedValue, "sent_at");
1639
1576
  return {
1640
1577
  version: RADAR_PUBLICATION_VERSION,
1641
- agent_id: agentId,
1578
+ registration_id: agentId,
1642
1579
  channel,
1643
1580
  publisher,
1644
1581
  ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
@@ -1679,12 +1616,12 @@ function createChannelWriter(input) {
1679
1616
  if (record.writer !== void 0) return true;
1680
1617
  const agentId = await input.subject(recovering);
1681
1618
  if (agentId === void 0) return false;
1682
- if (record.agent_id !== agentId) {
1619
+ if (record.registration_id !== agentId) {
1683
1620
  record = blank(agentId);
1684
1621
  save();
1685
1622
  }
1686
1623
  const answer = await client.acquire({
1687
- agent_id: agentId,
1624
+ registration_id: agentId,
1688
1625
  channel,
1689
1626
  publisher
1690
1627
  });
@@ -1714,7 +1651,7 @@ function createChannelWriter(input) {
1714
1651
  const writer = record.writer;
1715
1652
  if (writer === void 0) return;
1716
1653
  const answer = await client.publish({
1717
- agent_id: record.agent_id,
1654
+ registration_id: record.registration_id,
1718
1655
  channel,
1719
1656
  writer_handle: writer.handle,
1720
1657
  sequence: pending.sequence,
@@ -1808,9 +1745,9 @@ function createChannelWriter(input) {
1808
1745
  stopPump();
1809
1746
  const writer = record.writer;
1810
1747
  if (writer === void 0) return;
1811
- const agentId = record.agent_id;
1748
+ const agentId = record.registration_id;
1812
1749
  try {
1813
- void client.retire({ agent_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
1750
+ void client.retire({ registration_id: agentId, channel, writer_handle: writer.handle }).then((answer) => {
1814
1751
  if (answer.ok === true) {
1815
1752
  try {
1816
1753
  record.retired_at = new Date(scheduler.now()).toISOString();
@@ -1833,214 +1770,127 @@ function createChannelWriter(input) {
1833
1770
  diagnostic: () => record.diagnostic
1834
1771
  };
1835
1772
  }
1836
- function createContextWriter(input) {
1837
- const { store, client, scheduler, recordName, publisher } = input;
1838
- let record = loadRecord();
1839
- let desired;
1840
- let desiredMarker;
1841
- let running = false;
1842
- let again = false;
1843
- let recovering = false;
1844
- let stopped = false;
1845
- let fenced = false;
1846
- let pump;
1847
- function blank(agentId) {
1848
- return {
1849
- version: RADAR_PUBLICATION_VERSION,
1850
- agent_id: agentId,
1851
- publisher,
1852
- sequence: 0
1853
- };
1854
- }
1855
- function loadRecord() {
1856
- const value = store.read(recordName);
1857
- const agentId = textAt(value, "agent_id");
1858
- const storedPublisher = recordAt2(value, "publisher");
1859
- 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)
1860
- return blank("");
1861
- const writer = recordAt2(value, "writer");
1862
- const handle = textAt(writer, "handle");
1863
- const generation = numberAt2(writer, "generation");
1864
- if (writer !== void 0 && (!exactKeys(writer, WRITER_KEYS) || !isUuid(handle) || generation === void 0))
1865
- return blank(agentId);
1866
- const pending = contextReportAt(
1867
- recordAt2(value, "pending"),
1868
- CONTEXT_PENDING_KEYS,
1869
- CONTEXT_PENDING_MARKED_KEYS
1870
- );
1871
- const acceptedValue = recordAt2(value, "accepted");
1872
- const accepted = contextReportAt(
1873
- acceptedValue,
1874
- CONTEXT_ACCEPTED_KEYS,
1875
- CONTEXT_ACCEPTED_MARKED_KEYS
1876
- );
1877
- const sentAt = numberAt2(acceptedValue, "sent_at");
1878
- return {
1879
- version: RADAR_PUBLICATION_VERSION,
1880
- agent_id: agentId,
1881
- publisher,
1882
- ...handle !== void 0 && generation !== void 0 ? { writer: { handle, generation } } : {},
1883
- sequence: numberAt2(value, "sequence") ?? 0,
1884
- ...pending !== void 0 ? { pending } : {},
1885
- ...accepted !== void 0 && sentAt !== void 0 ? { accepted: { ...accepted, sent_at: sentAt } } : {},
1886
- ...textAt(value, "diagnostic") !== void 0 ? { diagnostic: textAt(value, "diagnostic") } : {}
1887
- };
1888
- }
1889
- function note(message) {
1890
- if (record.diagnostic !== void 0) return;
1891
- record.diagnostic = message.slice(0, 256);
1892
- save();
1773
+ function createPresenceReporter(input) {
1774
+ const { store, client, scheduler, recordName } = input;
1775
+ let state;
1776
+ let standing;
1777
+ let diagnostic;
1778
+ try {
1779
+ const saved = store.read(recordName);
1780
+ if (saved?.version === 2 && isUuid(saved.claim_id) && isUuid(saved.session) && Array.isArray(saved.releases))
1781
+ state = saved;
1782
+ } catch (error) {
1783
+ diagnostic = `presence storage unavailable: ${String(error)}`;
1893
1784
  }
1785
+ let timer;
1786
+ let stopped = false;
1787
+ let running;
1894
1788
  function save() {
1895
- store.write(recordName, record);
1896
- }
1897
- function fence(message) {
1898
- fenced = true;
1899
- stopPump();
1900
- note(`context stopped: ${message}`);
1901
- }
1902
- function startPump() {
1903
- if (pump !== void 0 || stopped || fenced) return;
1904
- pump = scheduler.setInterval(() => kick(), input.heartbeatMs);
1905
- pump.unref?.();
1906
- }
1907
- function stopPump() {
1908
- if (pump === void 0) return;
1909
- scheduler.clearInterval(pump);
1910
- pump = void 0;
1789
+ try {
1790
+ if (state) store.write(recordName, state);
1791
+ return true;
1792
+ } catch (error) {
1793
+ diagnostic = `presence storage unavailable: ${String(error)}`;
1794
+ return false;
1795
+ }
1911
1796
  }
1912
- async function ensureSubject() {
1913
- if (record.writer !== void 0) return true;
1914
- const agentId = await input.subject(recovering);
1915
- if (agentId === void 0) return false;
1916
- if (record.agent_id !== agentId) {
1917
- record = blank(agentId);
1797
+ async function releasePending() {
1798
+ if (!state) return;
1799
+ const registration = await input.registration();
1800
+ if (!registration?.process) return;
1801
+ for (const release of [...state.releases]) {
1802
+ const answer = await client.releasePresence({ ...release, process: registration.process });
1803
+ if (!answer.ok) return;
1804
+ state.releases = state.releases.filter((r) => r.claim_id !== release.claim_id);
1918
1805
  save();
1919
1806
  }
1920
- return true;
1921
1807
  }
1922
- function heartbeatDue() {
1923
- return record.accepted !== void 0 && scheduler.now() - record.accepted.sent_at >= input.heartbeatMs;
1924
- }
1925
- async function send(pending) {
1926
- const writer = record.writer;
1927
- const answer = await client.context({
1928
- agent_id: record.agent_id,
1929
- publisher,
1930
- // A first publish presents no handle: the daemon binds the writer the
1931
- // publisher already is, which is the binding this writer then keeps.
1932
- ...writer !== void 0 ? { writer_handle: writer.handle } : {},
1933
- sequence: pending.sequence,
1934
- lease_ms: pending.lease_ms,
1935
- observed_at: pending.observed_at,
1936
- context: {
1937
- session: pending.session,
1938
- ...pending.launch_identity !== void 0 ? { launch_identity: pending.launch_identity } : {}
1808
+ function schedule() {
1809
+ if (stopped || timer) return;
1810
+ timer = scheduler.setTimeout(() => {
1811
+ timer = void 0;
1812
+ void ensure();
1813
+ }, input.heartbeatMs);
1814
+ timer.unref?.();
1815
+ }
1816
+ async function ensure() {
1817
+ if (running) return running;
1818
+ if (stopped || !state) return void 0;
1819
+ const claim = state;
1820
+ if (!save()) return void 0;
1821
+ running = (async () => {
1822
+ await releasePending();
1823
+ const registration = await input.registration();
1824
+ if (!registration?.process || state !== claim || stopped) return void 0;
1825
+ const answer = await client.presence({
1826
+ ...registration,
1827
+ process: registration.process,
1828
+ session_id: claim.session,
1829
+ claim_id: claim.claim_id,
1830
+ ...claim.registration_id ? { registration_id: claim.registration_id } : {},
1831
+ ...claim.subject_id ? { subject_id: claim.subject_id } : {}
1832
+ });
1833
+ if (state !== claim) return void 0;
1834
+ if (!answer.ok) {
1835
+ if (!["absent", "timeout", "transport"].includes(answer.code)) diagnostic = answer.message;
1836
+ return void 0;
1939
1837
  }
1940
- });
1941
- if (answer.ok === true) {
1942
- recovering = false;
1943
- record.writer = {
1944
- handle: answer.value.writer.handle,
1945
- generation: answer.value.writer.generation
1946
- };
1947
- record.sequence = Math.max(record.sequence, answer.value.writer.sequence);
1948
- record.accepted = {
1949
- ...pending,
1950
- sent_at: answer.value.warning === void 0 ? scheduler.now() : record.accepted?.sent_at ?? scheduler.now()
1951
- };
1952
- record.pending = void 0;
1838
+ claim.registration_id = answer.value.registration_id;
1839
+ claim.subject_id = answer.value.standing === "granted" ? answer.value.subject_id ?? void 0 : void 0;
1840
+ 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" };
1841
+ diagnostic = answer.value.standing === "duplicate" ? DUPLICATE_SESSION_DIAGNOSTIC : void 0;
1953
1842
  save();
1954
- return;
1955
- }
1956
- if (answer.code === "refused" || answer.code === "bad_params" || // A daemon that speaks the registry but does not serve this record would
1957
- // refuse every retry the same way; one diagnostic beats a silent retry
1958
- // loop once per heartbeat.
1959
- answer.code === "unknown_method") {
1960
- fence(answer.message);
1961
- return;
1962
- }
1963
- if (answer.code === "not_found") {
1964
- record = blank("");
1965
- recovering = true;
1966
- save();
1967
- return;
1968
- }
1969
- }
1970
- async function stage(session, launchIdentity) {
1971
- const pending = {
1972
- sequence: record.sequence + 1,
1973
- session,
1974
- lease_ms: input.leaseMs,
1975
- observed_at: new Date(scheduler.now()).toISOString(),
1976
- ...launchIdentity !== void 0 ? { launch_identity: launchIdentity } : {}
1977
- };
1978
- record.pending = pending;
1979
- save();
1980
- await send(pending);
1981
- }
1982
- async function drainOnce() {
1983
- if (fenced) return;
1984
- if (record.pending !== void 0) {
1985
- await send(record.pending);
1986
- return;
1987
- }
1988
- if (desired === void 0) return;
1989
- if (!await ensureSubject()) return;
1990
- if (fenced) return;
1991
- if (record.writer === void 0 || record.accepted === void 0 || record.accepted.session !== desired || !sameLaunchIdentity(record.accepted.launch_identity, desiredMarker)) {
1992
- await stage(desired, desiredMarker);
1993
- return;
1994
- }
1995
- if (heartbeatDue()) await stage(record.accepted.session, desiredMarker);
1996
- }
1997
- async function drain() {
1998
- if (running || stopped || fenced) return;
1999
- running = true;
1843
+ return claim.registration_id;
1844
+ })();
2000
1845
  try {
2001
- await drainOnce();
1846
+ return await running;
2002
1847
  } catch (error) {
2003
- fenced = true;
2004
- stopPump();
2005
- try {
2006
- note(`context storage failure: ${String(error).slice(0, 180)}`);
2007
- } catch {
2008
- }
1848
+ diagnostic = `presence unavailable: ${String(error)}`;
1849
+ return void 0;
2009
1850
  } finally {
2010
- running = false;
2011
- if (again && !stopped && !fenced) {
2012
- again = false;
2013
- kick();
2014
- }
1851
+ running = void 0;
1852
+ if (!stopped && state !== claim) void ensure();
1853
+ else schedule();
2015
1854
  }
2016
1855
  }
2017
- function kick() {
2018
- if (stopped || fenced) return;
2019
- startPump();
2020
- if (running) {
2021
- again = true;
2022
- return;
2023
- }
2024
- void drain().catch(() => void 0);
2025
- }
2026
1856
  return {
2027
- update(session, launchIdentity) {
2028
- if (fenced || session === desired && sameLaunchIdentity(desiredMarker, launchIdentity))
2029
- return;
2030
- desired = session;
2031
- desiredMarker = launchIdentity;
2032
- if (stopped) return;
2033
- kick();
1857
+ report(session) {
1858
+ const releases = [...state?.releases ?? []];
1859
+ if (state?.registration_id && !releases.some((release) => release.claim_id === state.claim_id)) {
1860
+ releases.push({ registration_id: state.registration_id, claim_id: state.claim_id });
1861
+ }
1862
+ standing = void 0;
1863
+ state = { version: 2, session, claim_id: randomUUID2(), releases };
1864
+ save();
1865
+ stopped = false;
1866
+ void ensure();
1867
+ },
1868
+ ensure,
1869
+ async release() {
1870
+ const releasing = state;
1871
+ standing = void 0;
1872
+ stopped = true;
1873
+ if (timer) scheduler.clearTimeout(timer);
1874
+ timer = void 0;
1875
+ if (running) await running;
1876
+ if (state === releasing) standing = void 0;
1877
+ if (releasing?.registration_id && !releasing.releases.some((r) => r.claim_id === releasing.claim_id)) {
1878
+ releasing.releases.push({ registration_id: releasing.registration_id, claim_id: releasing.claim_id });
1879
+ if (!save()) return;
1880
+ }
1881
+ await releasePending();
2034
1882
  },
2035
1883
  stop() {
2036
1884
  stopped = true;
2037
- stopPump();
1885
+ if (timer) scheduler.clearTimeout(timer);
1886
+ timer = void 0;
2038
1887
  },
2039
1888
  reopen() {
2040
1889
  stopped = false;
2041
- if (desired !== void 0 || record.pending !== void 0) kick();
1890
+ void ensure();
2042
1891
  },
2043
- diagnostic: () => record.diagnostic
1892
+ management: () => standing,
1893
+ diagnostic: () => diagnostic
2044
1894
  };
2045
1895
  }
2046
1896
  var STORE_IDENTITY = /* @__PURE__ */ new WeakMap();
@@ -2069,10 +1919,13 @@ function unavailablePublication(error) {
2069
1919
  };
2070
1920
  return {
2071
1921
  execution: writer,
2072
- context: {
2073
- update: () => void 0,
1922
+ presence: {
1923
+ report: () => void 0,
1924
+ ensure: async () => void 0,
1925
+ release: async () => void 0,
2074
1926
  stop: () => void 0,
2075
1927
  reopen: () => void 0,
1928
+ management: () => void 0,
2076
1929
  diagnostic: () => diagnostic
2077
1930
  },
2078
1931
  facts: { ...writer, update: () => void 0 },
@@ -2099,33 +1952,29 @@ function createRadarPublicationInner(options) {
2099
1952
  cached.reopen();
2100
1953
  return cached;
2101
1954
  }
2102
- const subjectName = `subjects/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
1955
+ const registrationName = `registrations/${createHash("sha256").update(`${options.registration.source}\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2103
1956
  const channelRecord = (channel, target) => `channels/${createHash("sha256").update(`${channel}\0${incarnation}\0${target ?? ""}`).digest("hex").slice(0, 32)}.json`;
2104
- const contextRecord = `context/${createHash("sha256").update(`context\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
2105
- function readSubject() {
2106
- const value = store.read(subjectName);
1957
+ const presenceRecord = `presence/${createHash("sha256").update(`presence\0${incarnation}`).digest("hex").slice(0, 32)}.json`;
1958
+ function readRegistration() {
1959
+ const value = store.read(registrationName);
2107
1960
  const registration = recordAt2(value, "registration");
2108
1961
  if (numberAt2(value, "version") !== RADAR_PUBLICATION_VERSION || textAt(registration, "source") !== options.registration.source || textAt(registration, "incarnation") !== incarnation)
2109
1962
  return void 0;
2110
- const storedAgentId = textAt(value, "agent_id");
1963
+ const storedAgentId = textAt(value, "registration_id");
2111
1964
  return {
2112
1965
  version: RADAR_PUBLICATION_VERSION,
2113
1966
  registration,
2114
- ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { agent_id: storedAgentId } : {}
1967
+ ...storedAgentId !== void 0 && isUuid(storedAgentId) ? { registration_id: storedAgentId } : {}
2115
1968
  };
2116
1969
  }
2117
- const stored = readSubject();
2118
- let subject = stored;
1970
+ const stored = readRegistration();
1971
+ let processRecord = stored;
2119
1972
  let registrationDiagnostic;
2120
1973
  let registrationRecord;
2121
1974
  async function ensureRegistration() {
2122
- if (subject !== void 0) return subject;
1975
+ if (processRecord !== void 0) return processRecord;
2123
1976
  registrationRecord ??= (async () => {
2124
1977
  const resolution = options.location ? await options.location() : { state: "absent", reason: "this process publishes no location" };
2125
- if (resolution.state === "pending") {
2126
- registrationDiagnostic = `location bootstrap pending: ${resolution.reason}`.slice(0, 256);
2127
- return void 0;
2128
- }
2129
1978
  const built = {
2130
1979
  version: RADAR_PUBLICATION_VERSION,
2131
1980
  registration: {
@@ -2136,13 +1985,13 @@ function createRadarPublicationInner(options) {
2136
1985
  }
2137
1986
  };
2138
1987
  try {
2139
- store.write(subjectName, built);
1988
+ store.write(registrationName, built);
2140
1989
  } catch (error) {
2141
1990
  registrationDiagnostic ??= `private storage unavailable: ${String(error)}`.slice(0, 256);
2142
1991
  return void 0;
2143
1992
  }
2144
1993
  registrationDiagnostic = void 0;
2145
- subject = built;
1994
+ processRecord = built;
2146
1995
  return built;
2147
1996
  })();
2148
1997
  try {
@@ -2151,11 +2000,10 @@ function createRadarPublicationInner(options) {
2151
2000
  registrationRecord = void 0;
2152
2001
  }
2153
2002
  }
2154
- let agentId = stored?.agent_id;
2003
+ let agentId = stored?.registration_id;
2155
2004
  let subjectReady;
2156
2005
  async function ensureSubject(recheck) {
2157
- if (recheck) agentId = void 0;
2158
- if (agentId !== void 0) return agentId;
2006
+ if (!recheck && agentId !== void 0) return agentId;
2159
2007
  subjectReady ??= (async () => {
2160
2008
  const ready = await client.ping();
2161
2009
  if (ready.ok === false) {
@@ -2165,18 +2013,11 @@ function createRadarPublicationInner(options) {
2165
2013
  }
2166
2014
  const record = await ensureRegistration();
2167
2015
  if (record === void 0) return void 0;
2168
- const answer = await client.register({
2169
- ...record.registration,
2170
- ...spawnToken !== void 0 ? { spawn_token: spawnToken } : {}
2171
- });
2172
- if (answer.ok === false) {
2173
- if (answer.code !== "absent" && answer.code !== "timeout" && answer.code !== "transport")
2174
- registrationDiagnostic ??= `subject registration refused: ${answer.message}`.slice(0, 256);
2175
- return void 0;
2176
- }
2177
- agentId = answer.value.agent_id;
2178
- subject = { ...record, agent_id: agentId };
2179
- store.write(subjectName, subject);
2016
+ const registered = await presence.ensure();
2017
+ if (registered === void 0) return void 0;
2018
+ agentId = registered;
2019
+ processRecord = { ...record, registration_id: agentId };
2020
+ store.write(registrationName, processRecord);
2180
2021
  if (options.binding !== void 0) writeBinding(options.binding);
2181
2022
  return agentId;
2182
2023
  })();
@@ -2190,7 +2031,7 @@ function createRadarPublicationInner(options) {
2190
2031
  if (agentId === void 0) return;
2191
2032
  store.write(`bindings/${binding.key}.json`, {
2192
2033
  version: RADAR_PUBLICATION_VERSION,
2193
- agent_id: agentId,
2034
+ registration_id: agentId,
2194
2035
  incarnation,
2195
2036
  run: binding.run,
2196
2037
  owner: binding.owner,
@@ -2214,14 +2055,22 @@ function createRadarPublicationInner(options) {
2214
2055
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2215
2056
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2216
2057
  });
2217
- const context = createContextWriter({
2058
+ const presence = createPresenceReporter({
2218
2059
  store,
2219
2060
  client,
2220
2061
  scheduler,
2221
- recordName: contextRecord,
2222
- publisher: publisher("herdsman-pi"),
2223
- subject: ensureSubject,
2224
- leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2062
+ recordName: presenceRecord,
2063
+ registration: async () => {
2064
+ const record = await ensureRegistration();
2065
+ if (!record) return void 0;
2066
+ const location = await options.location?.();
2067
+ const { location: _initialLocation, ...identity } = record.registration;
2068
+ return {
2069
+ ...identity,
2070
+ ...location?.state === "resolved" ? { location: location.location } : {},
2071
+ ...spawnToken ? { spawn_token: spawnToken } : {}
2072
+ };
2073
+ },
2225
2074
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
2226
2075
  });
2227
2076
  const facts = createChannelWriter({
@@ -2239,7 +2088,7 @@ function createRadarPublicationInner(options) {
2239
2088
  const assignmentWriters = /* @__PURE__ */ new Map();
2240
2089
  const publication = {
2241
2090
  execution,
2242
- context,
2091
+ presence,
2243
2092
  facts,
2244
2093
  assignment(target, reportingOwner) {
2245
2094
  const existing = assignmentWriters.get(target);
@@ -2254,7 +2103,7 @@ function createRadarPublicationInner(options) {
2254
2103
  publisher: publisher("herdsman-owner", reportingOwner),
2255
2104
  codec: assignmentCodec,
2256
2105
  // The owner publishes about a subject it did not register: the exact
2257
- // agent_id from the child's sidecar is the whole correlation.
2106
+ // registration_id from the child's sidecar is the whole correlation.
2258
2107
  subject: async () => target,
2259
2108
  leaseMs: options.leaseMs ?? RADAR_LEASE_MS,
2260
2109
  heartbeatMs: options.heartbeatMs ?? RADAR_HEARTBEAT_MS
@@ -2274,7 +2123,7 @@ function createRadarPublicationInner(options) {
2274
2123
  registrationDiagnostic ??= `Radar binding unavailable: ${String(error).slice(0, 180)}`;
2275
2124
  return void 0;
2276
2125
  }
2277
- const bound = textAt(value, "agent_id");
2126
+ const bound = textAt(value, "registration_id");
2278
2127
  const boundIncarnation = textAt(value, "incarnation");
2279
2128
  const run = textAt(value, "run");
2280
2129
  const owner = textAt(value, "owner");
@@ -2284,7 +2133,7 @@ function createRadarPublicationInner(options) {
2284
2133
  return void 0;
2285
2134
  return {
2286
2135
  version: RADAR_PUBLICATION_VERSION,
2287
- agent_id: bound,
2136
+ registration_id: bound,
2288
2137
  incarnation: boundIncarnation,
2289
2138
  run,
2290
2139
  owner,
@@ -2300,25 +2149,27 @@ function createRadarPublicationInner(options) {
2300
2149
  if (agentId2 === void 0) return void 0;
2301
2150
  const record = await ensureRegistration();
2302
2151
  if (record === void 0) return void 0;
2303
- const location = record.registration.location;
2304
- const subjectId = process.env.PI_RADAR_MANAGED_SUBJECT_ID;
2152
+ const currentLocation = await options.location?.();
2153
+ const location = currentLocation?.state === "resolved" ? currentLocation.location : void 0;
2154
+ const standing = presence.management();
2155
+ const subjectId = standing?.state === "managed" ? standing.subject_id : void 0;
2305
2156
  return {
2306
- agent_id: agentId2,
2157
+ registration_id: agentId2,
2307
2158
  ...subjectId !== void 0 ? { subject_id: subjectId } : {},
2308
2159
  ...location !== void 0 ? { location } : {}
2309
2160
  };
2310
2161
  },
2311
- diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? context.diagnostic() ?? facts.diagnostic(),
2162
+ diagnostic: () => registrationDiagnostic ?? execution.diagnostic() ?? presence.diagnostic() ?? facts.diagnostic(),
2312
2163
  stop() {
2313
2164
  for (const writer of assignmentWriters.values()) writer.stop();
2314
2165
  execution.stop();
2315
- context.stop();
2166
+ presence.stop();
2316
2167
  facts.stop();
2317
2168
  },
2318
2169
  reopen() {
2319
2170
  for (const writer of assignmentWriters.values()) writer.reopen();
2320
2171
  execution.reopen();
2321
- context.reopen();
2172
+ presence.reopen();
2322
2173
  facts.reopen();
2323
2174
  }
2324
2175
  };
@@ -2350,10 +2201,13 @@ function unavailablePublication2() {
2350
2201
  return {
2351
2202
  execution: writer,
2352
2203
  facts: { ...writer, update: () => void 0 },
2353
- context: {
2354
- update: () => void 0,
2204
+ presence: {
2205
+ report: () => void 0,
2355
2206
  stop: () => void 0,
2356
2207
  reopen: () => void 0,
2208
+ management: () => void 0,
2209
+ ensure: async () => void 0,
2210
+ release: async () => void 0,
2357
2211
  diagnostic: () => "Radar private storage is unavailable"
2358
2212
  },
2359
2213
  assignment: () => writer,
@@ -2436,11 +2290,11 @@ function createRadarExecutionAdapter(pi, options) {
2436
2290
  ...lastOutcome !== void 0 ? { last_outcome: lastOutcome } : {}
2437
2291
  };
2438
2292
  }
2439
- function reportSession(context, launchIdentity) {
2293
+ function reportSession(context) {
2440
2294
  const session = context?.sessionManager?.getSessionId?.();
2441
2295
  if (!isUuid(session)) return;
2442
2296
  try {
2443
- publication.context.update(session, launchIdentity);
2297
+ publication.presence.report(session);
2444
2298
  } catch {
2445
2299
  }
2446
2300
  }
@@ -2504,38 +2358,8 @@ function createRadarExecutionAdapter(pi, options) {
2504
2358
  })
2505
2359
  );
2506
2360
  on("session_start", (_event, context) => {
2507
- const sessionHost = {
2508
- appendEntry: pi.appendEntry ? (customType, data) => pi.appendEntry?.(customType, data) : void 0,
2509
- sessionManager: context?.sessionManager
2510
- };
2511
- let launchIdentity;
2512
- try {
2513
- const result = persistRadarLaunchMarker(
2514
- sessionHost,
2515
- options.markerEnvironment
2516
- );
2517
- if (result === "written" || result === "unchanged") {
2518
- const expected = radarLaunchMarkerFromEnvironment(options.markerEnvironment);
2519
- const activeMarker = activeRadarLaunchMarker(sessionHost);
2520
- if (expected && activeMarker.status === "valid" && JSON.stringify(activeMarker.marker) === JSON.stringify(expected)) {
2521
- launchIdentity = activeMarker.marker;
2522
- } else {
2523
- options.diagnostic?.("Radar launch marker was not visible on the active Pi session branch");
2524
- }
2525
- } else if (result === "unsupported") {
2526
- options.diagnostic?.("Pi host does not support Radar launch identity session entries");
2527
- } else if (options.markerEnvironment && [
2528
- "PI_RADAR_MANAGED_SUBJECT_ID",
2529
- "PI_RADAR_MANAGED_LAUNCH_ID",
2530
- "PI_RADAR_MANAGED_EXECUTION_GENERATION"
2531
- ].some((key) => options.markerEnvironment?.[key] !== void 0)) {
2532
- options.diagnostic?.("incomplete or invalid Radar launch identity environment");
2533
- }
2534
- } catch (error) {
2535
- options.diagnostic?.(`could not persist Radar launch marker: ${String(error)}`);
2536
- }
2537
2361
  rootSession = true;
2538
- reportSession(context, launchIdentity);
2362
+ reportSession(context);
2539
2363
  publishFacts(context);
2540
2364
  publication.reopen();
2541
2365
  active = context?.isIdle?.() === false;
@@ -2566,8 +2390,9 @@ function createRadarExecutionAdapter(pi, options) {
2566
2390
  }
2567
2391
  emit();
2568
2392
  });
2569
- on("session_shutdown", () => {
2393
+ on("session_shutdown", async () => {
2570
2394
  suspend();
2395
+ await publication.presence.release();
2571
2396
  });
2572
2397
  function suspend() {
2573
2398
  if (stopped) return;