pi-radar 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +10 -4
  2. package/dist/extension/baseline.js +333 -394
  3. package/dist/extension/radar-client.js +126 -11
  4. package/dist/extension/radar-communication.js +39 -0
  5. package/dist/extension/radar-execution.js +169 -382
  6. package/dist/extension/radar-launch.js +18 -13
  7. package/dist/extension/radar-publication.js +161 -298
  8. package/dist/extension/radar-runtime.js +1 -1
  9. package/dist/index.js +356 -407
  10. package/extension/baseline.ts +2 -0
  11. package/extension/generated-contracts.ts +1310 -10
  12. package/extension/radar-client.ts +191 -23
  13. package/extension/radar-communication.ts +66 -0
  14. package/extension/radar-execution.ts +11 -58
  15. package/extension/radar-launch.ts +29 -22
  16. package/extension/radar-publication.ts +211 -410
  17. package/extension/radar-runtime.ts +4 -1
  18. package/index.ts +1 -0
  19. package/package.json +9 -5
  20. package/schema/public/v1/handoff-inbox.deserialize.schema.json +18 -0
  21. package/schema/public/v1/handoff-inbox.serialize.schema.json +18 -0
  22. package/schema/public/v1/handoff-publish-params.deserialize.schema.json +33 -0
  23. package/schema/public/v1/handoff-publish-params.serialize.schema.json +33 -0
  24. package/schema/public/v1/handoff-receipt.deserialize.schema.json +66 -0
  25. package/schema/public/v1/handoff-receipt.serialize.schema.json +66 -0
  26. package/schema/public/v1/handoff.deserialize.schema.json +84 -0
  27. package/schema/public/v1/handoff.serialize.schema.json +84 -0
  28. package/schema/public/v1/observation-get-params.deserialize.schema.json +46 -0
  29. package/schema/public/v1/observation-get-params.serialize.schema.json +52 -0
  30. package/schema/public/v1/observation-get-response.deserialize.schema.json +2731 -0
  31. package/schema/public/v1/observation-get-response.serialize.schema.json +2862 -0
  32. package/schema/public/v1/observation-snapshot.deserialize.schema.json +2616 -0
  33. package/schema/public/v1/observation-snapshot.serialize.schema.json +2741 -0
  34. package/schema/public/v1/presence-answer.deserialize.schema.json +40 -0
  35. package/schema/public/v1/presence-answer.serialize.schema.json +40 -0
  36. package/schema/public/v1/presence-release.deserialize.schema.json +51 -0
  37. package/schema/public/v1/presence-release.serialize.schema.json +52 -0
  38. package/schema/public/v1/presence-report.deserialize.schema.json +149 -0
  39. package/schema/public/v1/presence-report.serialize.schema.json +157 -0
  40. package/extension/radar-session-marker.ts +0 -146
  41. package/schema/public/v1/launch-identity-marker.deserialize.schema.json +0 -32
  42. package/schema/public/v1/launch-identity-marker.serialize.schema.json +0 -32
@@ -11,6 +11,7 @@ var RADAR_SPAWN_CONTRACT = "spawn.v1";
11
11
  var RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
12
12
  var RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
13
13
  var RADAR_EVENTS_CONTRACT = "events.v1";
14
+ var RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
14
15
  var RADAR_REQUESTER = "pi-radar";
15
16
  var RADAR_MAX_LINE_BYTES = 1024 * 1024;
16
17
  var RADAR_CALL_TIMEOUT_MS = 2e3;
@@ -108,6 +109,25 @@ function recordAt(value, field) {
108
109
  function isUuid(value) {
109
110
  return typeof value === "string" && UUID.test(value);
110
111
  }
112
+ function decodePresenceAnswer(value) {
113
+ const registrationId = recordAt(value, "registration_id");
114
+ const claimId = recordAt(value, "claim_id");
115
+ const standing = recordAt(value, "standing");
116
+ const subjectId = recordAt(value, "subject_id");
117
+ if (!isUuid(registrationId) || !isUuid(claimId) || !["pending", "granted", "duplicate", "released"].includes(String(standing)) || subjectId !== void 0 && !isUuid(subjectId) || standing === "granted" && !isUuid(subjectId))
118
+ return protocol("presence reply carries no valid claim standing");
119
+ return { ok: true, value };
120
+ }
121
+ function managementAt(value) {
122
+ const field = recordAt(value, "management");
123
+ const state = stringAt(field, "state");
124
+ if (state === "managed") {
125
+ const subjectId = stringAt(field, "subject_id");
126
+ return subjectId === void 0 ? void 0 : { state, subject_id: subjectId };
127
+ }
128
+ if (state === "duplicate" || state === "pending" || state === "unmanaged") return { state };
129
+ return void 0;
130
+ }
111
131
  function stringAt(value, field) {
112
132
  if (typeof value !== "object" || value === null) return void 0;
113
133
  const found = value[field];
@@ -118,6 +138,28 @@ function numberAt(value, field) {
118
138
  const found = value[field];
119
139
  return typeof found === "number" && Number.isFinite(found) ? found : void 0;
120
140
  }
141
+ function handoffAt(value) {
142
+ const version = numberAt(value, "version");
143
+ const id = stringAt(value, "id");
144
+ const sender = recordAt(value, "sender");
145
+ const senderGeneration = numberAt(sender, "execution_generation");
146
+ const target = stringAt(value, "recipient_subject_id");
147
+ const summary = stringAt(value, "summary");
148
+ const content = stringAt(value, "content");
149
+ const published = numberAt(value, "published_at_unix_ms");
150
+ const correlation = typeof value === "object" && value !== null ? value.correlation_id : void 0;
151
+ 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;
152
+ return value;
153
+ }
154
+ function handoffReceiptAt(value) {
155
+ const version = numberAt(value, "version");
156
+ const handoffId = stringAt(value, "handoff_id");
157
+ const recipient = recordAt(value, "recipient");
158
+ const generation = numberAt(recipient, "execution_generation");
159
+ const received = numberAt(value, "received_at_unix_ms");
160
+ 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;
161
+ return value;
162
+ }
121
163
  function writerBinding(value) {
122
164
  const handle = stringAt(value, "handle");
123
165
  const source = stringAt(value, "source");
@@ -227,7 +269,8 @@ var RADAR_SPAWN_TOKEN_ENV = "PI_RADAR_SPAWN_TOKEN";
227
269
  var RADAR_MANAGED_IDENTITY_ENV = /* @__PURE__ */ new Set([
228
270
  "PI_RADAR_MANAGED_SUBJECT_ID",
229
271
  "PI_RADAR_MANAGED_LAUNCH_ID",
230
- "PI_RADAR_MANAGED_EXECUTION_GENERATION"
272
+ "PI_RADAR_MANAGED_EXECUTION_GENERATION",
273
+ "PI_RADAR_MANAGED_PARENT_SUBJECT_ID"
231
274
  ]);
232
275
  function unresolvedSpawn(requestId, detail) {
233
276
  return {
@@ -272,7 +315,7 @@ function spawnEdge(value) {
272
315
  const bound = recordAt(value, "bound");
273
316
  const source = stringAt(bound, "source");
274
317
  const incarnation = stringAt(bound, "incarnation");
275
- const agentId = stringAt(bound, "agent_id");
318
+ const agentId = stringAt(bound, "registration_id");
276
319
  const exit = exitAt(recordAt(value, "exit_observation"));
277
320
  return {
278
321
  ok: true,
@@ -286,7 +329,7 @@ function spawnEdge(value) {
286
329
  bound: {
287
330
  source,
288
331
  incarnation,
289
- ...isUuid(agentId) ? { agent_id: agentId } : {}
332
+ ...isUuid(agentId) ? { registration_id: agentId } : {}
290
333
  }
291
334
  } : {},
292
335
  ...exit !== void 0 ? { exit_observation: exit } : {}
@@ -684,6 +727,22 @@ function createRadarClient(options = {}) {
684
727
  return {
685
728
  socketPath,
686
729
  ping,
730
+ async presence(request) {
731
+ return withRegistry(async () => {
732
+ const answer = await call("agent.presence", { ...request });
733
+ if (!answer.ok) return answer;
734
+ return decodePresenceAnswer(answer.value);
735
+ });
736
+ },
737
+ async releasePresence(request) {
738
+ return withRegistry(async () => {
739
+ const answer = await call("agent.release", { ...request });
740
+ if (!answer.ok) return answer;
741
+ const decoded = decodePresenceAnswer(answer.value);
742
+ if (!decoded.ok) return decoded;
743
+ return decoded.value.standing === "released" ? decoded : protocol("release reply did not confirm the claim release");
744
+ });
745
+ },
687
746
  async register(registration) {
688
747
  return withRegistry(async () => {
689
748
  const answer = await call("agent.register", {
@@ -699,15 +758,15 @@ function createRadarClient(options = {}) {
699
758
  });
700
759
  if (answer.ok === false) return answer;
701
760
  const record = answer.value.registration;
702
- const agentId = stringAt(record, "agent_id");
703
- if (!isUuid(agentId)) return protocol("registration reply carries no agent_id");
704
- return { ok: true, value: { agent_id: agentId } };
761
+ const agentId = stringAt(record, "registration_id");
762
+ if (!isUuid(agentId)) return protocol("registration reply carries no registration_id");
763
+ return { ok: true, value: { registration_id: agentId } };
705
764
  });
706
765
  },
707
766
  async acquire(request) {
708
767
  return withRegistryChannel(request.channel, async () => {
709
768
  const answer = await call("agent.acquire", {
710
- agent_id: request.agent_id,
769
+ registration_id: request.registration_id,
711
770
  channel: request.channel,
712
771
  publisher: request.publisher,
713
772
  ...request.replace !== void 0 ? { replace: request.replace } : {}
@@ -736,7 +795,7 @@ function createRadarClient(options = {}) {
736
795
  }
737
796
  return withRegistryChannel(request.channel, async () => {
738
797
  const answer = await call("agent.publish", {
739
- agent_id: request.agent_id,
798
+ registration_id: request.registration_id,
740
799
  channel: request.channel,
741
800
  writer_handle: request.writer_handle,
742
801
  sequence: request.sequence,
@@ -755,7 +814,7 @@ function createRadarClient(options = {}) {
755
814
  async context(request) {
756
815
  return withRegistry(async () => {
757
816
  const answer = await call("agent.context", {
758
- agent_id: request.agent_id,
817
+ registration_id: request.registration_id,
759
818
  publisher: request.publisher,
760
819
  ...request.writer_handle !== void 0 ? { writer_handle: request.writer_handle } : {},
761
820
  ...request.replace !== void 0 ? { replace: request.replace } : {},
@@ -771,9 +830,14 @@ function createRadarClient(options = {}) {
771
830
  if (writer === void 0)
772
831
  return protocol("context reply carries no writer binding");
773
832
  const warning = stringAt(answer.value, "warning");
833
+ const management = managementAt(answer.value);
774
834
  return {
775
835
  ok: true,
776
- value: { writer, ...warning !== void 0 ? { warning } : {} }
836
+ value: {
837
+ writer,
838
+ ...warning !== void 0 ? { warning } : {},
839
+ ...management !== void 0 ? { management } : {}
840
+ }
777
841
  };
778
842
  });
779
843
  },
@@ -850,7 +914,7 @@ function createRadarClient(options = {}) {
850
914
  message: "agent.get requires a canonical agent UUID"
851
915
  };
852
916
  return withRegistry(async () => {
853
- const answer = await call("agent.get", { agent_id: agentId });
917
+ const answer = await call("agent.get", { registration_id: agentId });
854
918
  if (answer.ok === false) return answer;
855
919
  return agentProcessAt(recordAt(answer.value, "agent"));
856
920
  });
@@ -901,6 +965,56 @@ function createRadarClient(options = {}) {
901
965
  return protocol("spawn.close reply carries no close outcome");
902
966
  });
903
967
  },
968
+ async communicationPublish(request) {
969
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
970
+ const answer = await call("communication.publish", {
971
+ sender_registration_id: request.sender_registration_id,
972
+ recipient_subject_id: request.recipient_subject_id,
973
+ ...request.correlation_id !== void 0 ? { correlation_id: request.correlation_id } : {},
974
+ summary: request.summary,
975
+ content: request.content
976
+ }, request.request_id);
977
+ if (answer.ok === false) return answer;
978
+ const handoff = handoffAt(recordAt(answer.value, "handoff"));
979
+ const created = recordAt(answer.value, "created");
980
+ if (handoff === void 0 || typeof created !== "boolean")
981
+ return protocol("communication.publish reply carries no handoff");
982
+ return { ok: true, value: { handoff, created } };
983
+ });
984
+ },
985
+ async communicationInbox(request) {
986
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
987
+ const answer = await call("communication.inbox", request);
988
+ if (answer.ok === false) return answer;
989
+ const handoffIds = recordAt(answer.value, "handoff_ids");
990
+ if (!Array.isArray(handoffIds) || handoffIds.some((id) => !isUuid(id)))
991
+ return protocol("communication.inbox reply carries malformed handoff IDs");
992
+ return { ok: true, value: answer.value };
993
+ });
994
+ },
995
+ async communicationGet(request) {
996
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
997
+ const answer = await call("communication.get", request);
998
+ if (answer.ok === false) return answer;
999
+ const handoff = handoffAt(recordAt(answer.value, "handoff"));
1000
+ const receiptValue = recordAt(answer.value, "receipt");
1001
+ const receipt = receiptValue === null ? null : handoffReceiptAt(receiptValue);
1002
+ if (handoff === void 0 || receiptValue !== null && receipt === void 0)
1003
+ return protocol("communication.get reply carries malformed content or receipt");
1004
+ return { ok: true, value: { handoff, receipt: receipt ?? null } };
1005
+ });
1006
+ },
1007
+ async communicationAck(request) {
1008
+ return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
1009
+ const answer = await call("communication.ack", request);
1010
+ if (answer.ok === false) return answer;
1011
+ const receipt = handoffReceiptAt(recordAt(answer.value, "receipt"));
1012
+ const created = recordAt(answer.value, "created");
1013
+ if (receipt === void 0 || typeof created !== "boolean")
1014
+ return protocol("communication.ack reply carries no receipt");
1015
+ return { ok: true, value: { receipt, created } };
1016
+ });
1017
+ },
904
1018
  async events(request) {
905
1019
  if (!Number.isSafeInteger(request.after) || request.after < 0 || !Number.isSafeInteger(request.wait_ms) || request.wait_ms < 0 || request.wait_ms > RADAR_EVENT_WAIT_MS)
906
1020
  return {
@@ -1122,6 +1236,7 @@ function boundedRadarSnapshot(snapshot) {
1122
1236
  }
1123
1237
  export {
1124
1238
  RADAR_CALL_TIMEOUT_MS,
1239
+ RADAR_COMMUNICATION_CONTRACT,
1125
1240
  RADAR_EVENTS_CONTRACT,
1126
1241
  RADAR_EVENT_RETRY_MS,
1127
1242
  RADAR_MAX_AWAITED,
@@ -0,0 +1,39 @@
1
+ // extension/radar-communication.ts
2
+ function createRadarHandoffPort(client, publication, parentSubjectId = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID) {
3
+ async function agentId() {
4
+ const subject = await publication.subject();
5
+ return subject === void 0 ? { ok: false, code: "absent", message: "this process has no current Radar registration" } : { ok: true, value: subject.registration_id };
6
+ }
7
+ return {
8
+ async publish(request) {
9
+ const identity = await agentId();
10
+ if (identity.ok === false) return identity;
11
+ const parent = parentSubjectId();
12
+ if (parent === void 0)
13
+ return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
14
+ return client.communicationPublish({
15
+ ...request,
16
+ sender_registration_id: identity.value,
17
+ recipient_subject_id: parent
18
+ });
19
+ },
20
+ async inbox() {
21
+ const identity = await agentId();
22
+ if (identity.ok === false) return identity;
23
+ return client.communicationInbox({ registration_id: identity.value });
24
+ },
25
+ async get(handoff_id) {
26
+ const identity = await agentId();
27
+ if (identity.ok === false) return identity;
28
+ return client.communicationGet({ registration_id: identity.value, handoff_id });
29
+ },
30
+ async ack(handoff_id) {
31
+ const identity = await agentId();
32
+ if (identity.ok === false) return identity;
33
+ return client.communicationAck({ registration_id: identity.value, handoff_id });
34
+ }
35
+ };
36
+ }
37
+ export {
38
+ createRadarHandoffPort
39
+ };