pi-radar 0.2.0 → 0.3.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.
- package/README.md +8 -4
- package/dist/extension/baseline.js +116 -2
- package/dist/extension/radar-client.js +76 -1
- package/dist/extension/radar-communication.js +39 -0
- package/dist/extension/radar-execution.js +2 -0
- package/dist/extension/radar-launch.js +5 -0
- package/dist/extension/radar-publication.js +2 -0
- package/dist/extension/radar-runtime.js +1 -1
- package/dist/index.js +126 -2
- package/extension/baseline.ts +2 -0
- package/extension/generated-contracts.ts +64 -0
- package/extension/radar-client.ts +105 -0
- package/extension/radar-communication.ts +66 -0
- package/extension/radar-launch.ts +8 -1
- package/extension/radar-publication.ts +4 -0
- package/extension/radar-runtime.ts +4 -1
- package/index.ts +1 -0
- package/package.json +9 -4
- package/schema/public/v1/handoff-inbox.deserialize.schema.json +18 -0
- package/schema/public/v1/handoff-inbox.serialize.schema.json +18 -0
- package/schema/public/v1/handoff-publish-params.deserialize.schema.json +33 -0
- package/schema/public/v1/handoff-publish-params.serialize.schema.json +33 -0
- package/schema/public/v1/handoff-receipt.deserialize.schema.json +66 -0
- package/schema/public/v1/handoff-receipt.serialize.schema.json +66 -0
- package/schema/public/v1/handoff.deserialize.schema.json +84 -0
- package/schema/public/v1/handoff.serialize.schema.json +84 -0
|
@@ -19,6 +19,70 @@ export interface TaskLaneStatus {
|
|
|
19
19
|
available: boolean;
|
|
20
20
|
error: string | null;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Inputs for publishing an immutable handoff. The wire request ID carries the
|
|
24
|
+
* stable communication identity; the daemon derives the sender execution.
|
|
25
|
+
*/
|
|
26
|
+
export interface HandoffPublishParamsDeserialize {
|
|
27
|
+
content: string;
|
|
28
|
+
correlation_id?: string | null;
|
|
29
|
+
recipient_subject_id: string;
|
|
30
|
+
sender_agent_id: string;
|
|
31
|
+
summary: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A bounded inbox page by stable handoff ID. Payloads are fetched one at a
|
|
35
|
+
* time with `communication.get`, keeping each response within the wire bound.
|
|
36
|
+
*/
|
|
37
|
+
export interface HandoffInboxSerialize {
|
|
38
|
+
handoff_ids: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Immutable handoff content. The request id is the stable communication id.
|
|
42
|
+
*/
|
|
43
|
+
export interface HandoffSerialize {
|
|
44
|
+
content: string;
|
|
45
|
+
correlation_id?: string | null;
|
|
46
|
+
id: string;
|
|
47
|
+
published_at_unix_ms: number;
|
|
48
|
+
recipient_subject_id: string;
|
|
49
|
+
sender: HandoffIdentity;
|
|
50
|
+
summary: string;
|
|
51
|
+
version: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The current managed Pi execution that published or received a handoff.
|
|
55
|
+
* `subject_id` is stable across verified successors; `execution_id` and
|
|
56
|
+
* generation identify the exact execution that took the action.
|
|
57
|
+
*/
|
|
58
|
+
export interface HandoffIdentity {
|
|
59
|
+
agent_id: string;
|
|
60
|
+
execution_generation: number;
|
|
61
|
+
execution_id: string;
|
|
62
|
+
session_id: string;
|
|
63
|
+
subject_id: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Durable recipient-consumer receipt, stored separately from immutable content.
|
|
67
|
+
*/
|
|
68
|
+
export interface HandoffReceiptSerialize {
|
|
69
|
+
handoff_id: string;
|
|
70
|
+
received_at_unix_ms: number;
|
|
71
|
+
recipient: HandoffIdentity;
|
|
72
|
+
version: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The current managed Pi execution that published or received a handoff.
|
|
76
|
+
* `subject_id` is stable across verified successors; `execution_id` and
|
|
77
|
+
* generation identify the exact execution that took the action.
|
|
78
|
+
*/
|
|
79
|
+
export interface HandoffIdentity {
|
|
80
|
+
agent_id: string;
|
|
81
|
+
execution_generation: number;
|
|
82
|
+
execution_id: string;
|
|
83
|
+
session_id: string;
|
|
84
|
+
subject_id: string;
|
|
85
|
+
}
|
|
22
86
|
/**
|
|
23
87
|
* Managed owner/parent metadata accepted with a spawn intent. Domain validation
|
|
24
88
|
* and parent-binding checks remain in the daemon handler.
|
|
@@ -6,6 +6,10 @@ import type {
|
|
|
6
6
|
DaemonPingSerialize,
|
|
7
7
|
LaunchIdentityMarkerSerialize,
|
|
8
8
|
FactsSnapshotDeserialize,
|
|
9
|
+
HandoffInboxSerialize,
|
|
10
|
+
HandoffReceiptSerialize,
|
|
11
|
+
HandoffPublishParamsDeserialize,
|
|
12
|
+
HandoffSerialize,
|
|
9
13
|
ManagedLaunchMetadataDeserialize,
|
|
10
14
|
SpawnExitObservationSerialize,
|
|
11
15
|
} from "./generated-contracts.ts";
|
|
@@ -36,6 +40,7 @@ export const RADAR_SPAWN_CONTRACT = "spawn.v1";
|
|
|
36
40
|
export const RADAR_SPAWN_CLOSE_CONTRACT = "spawn.close.v1";
|
|
37
41
|
export const RADAR_SPAWN_LAUNCH_CONTEXT_CONTRACT = "spawn.launch_context.v1";
|
|
38
42
|
export const RADAR_EVENTS_CONTRACT = "events.v1";
|
|
43
|
+
export const RADAR_COMMUNICATION_CONTRACT = "communication.handoff.v1";
|
|
39
44
|
/** The default audit identity; callers can supply their own requester. */
|
|
40
45
|
export const RADAR_REQUESTER = "pi-radar";
|
|
41
46
|
/** The daemon's own line bound; nothing longer is sent or accepted. */
|
|
@@ -162,6 +167,9 @@ export type RadarBackgroundTask = NonNullable<
|
|
|
162
167
|
FactsSnapshotDeserialize["background_tasks"]
|
|
163
168
|
>[number];
|
|
164
169
|
export type RadarFactsSnapshot = FactsSnapshotDeserialize;
|
|
170
|
+
export type RadarHandoff = HandoffSerialize;
|
|
171
|
+
export type RadarHandoffInbox = HandoffInboxSerialize;
|
|
172
|
+
export type RadarHandoffReceipt = HandoffReceiptSerialize;
|
|
165
173
|
|
|
166
174
|
/**
|
|
167
175
|
* The assignment channel's interim fields. `request` is the owner's active
|
|
@@ -508,6 +516,46 @@ function numberAt(value: unknown, field: string): number | undefined {
|
|
|
508
516
|
: undefined;
|
|
509
517
|
}
|
|
510
518
|
|
|
519
|
+
function handoffAt(value: unknown): HandoffSerialize | undefined {
|
|
520
|
+
const version = numberAt(value, "version");
|
|
521
|
+
const id = stringAt(value, "id");
|
|
522
|
+
const sender = recordAt(value, "sender");
|
|
523
|
+
const senderGeneration = numberAt(sender, "execution_generation");
|
|
524
|
+
const target = stringAt(value, "recipient_subject_id");
|
|
525
|
+
const summary = stringAt(value, "summary");
|
|
526
|
+
const content = stringAt(value, "content");
|
|
527
|
+
const published = numberAt(value, "published_at_unix_ms");
|
|
528
|
+
const correlation = typeof value === "object" && value !== null
|
|
529
|
+
? (value as Record<string, unknown>).correlation_id
|
|
530
|
+
: undefined;
|
|
531
|
+
if (
|
|
532
|
+
version !== 1 || !isUuid(id) || !isUuid(stringAt(sender, "agent_id")) ||
|
|
533
|
+
!isUuid(stringAt(sender, "session_id")) || !isUuid(stringAt(sender, "subject_id")) ||
|
|
534
|
+
!isUuid(stringAt(sender, "execution_id")) || !Number.isSafeInteger(senderGeneration) ||
|
|
535
|
+
(senderGeneration ?? 0) < 1 || !isUuid(target) || summary === undefined || content === undefined ||
|
|
536
|
+
published === undefined || !Number.isSafeInteger(published) || published < 0 ||
|
|
537
|
+
(correlation !== undefined && correlation !== null &&
|
|
538
|
+
(typeof correlation !== "string" || correlation.length === 0 ||
|
|
539
|
+
Buffer.byteLength(correlation, "utf8") > 256 || /\p{Cc}/u.test(correlation)))
|
|
540
|
+
) return undefined;
|
|
541
|
+
return value as HandoffSerialize;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function handoffReceiptAt(value: unknown): HandoffReceiptSerialize | undefined {
|
|
545
|
+
const version = numberAt(value, "version");
|
|
546
|
+
const handoffId = stringAt(value, "handoff_id");
|
|
547
|
+
const recipient = recordAt(value, "recipient");
|
|
548
|
+
const generation = numberAt(recipient, "execution_generation");
|
|
549
|
+
const received = numberAt(value, "received_at_unix_ms");
|
|
550
|
+
if (
|
|
551
|
+
version !== 1 || !isUuid(handoffId) || !isUuid(stringAt(recipient, "agent_id")) ||
|
|
552
|
+
!isUuid(stringAt(recipient, "session_id")) || !isUuid(stringAt(recipient, "subject_id")) ||
|
|
553
|
+
!isUuid(stringAt(recipient, "execution_id")) || !Number.isSafeInteger(generation) ||
|
|
554
|
+
(generation ?? 0) < 1 || received === undefined || !Number.isSafeInteger(received) || received < 0
|
|
555
|
+
) return undefined;
|
|
556
|
+
return value as HandoffReceiptSerialize;
|
|
557
|
+
}
|
|
558
|
+
|
|
511
559
|
/** A writer binding a channel or context reply hands back to its publisher. */
|
|
512
560
|
function writerBinding(value: unknown): RadarWriterBinding | undefined {
|
|
513
561
|
const handle = stringAt(value, "handle");
|
|
@@ -662,6 +710,7 @@ const RADAR_MANAGED_IDENTITY_ENV = new Set([
|
|
|
662
710
|
"PI_RADAR_MANAGED_SUBJECT_ID",
|
|
663
711
|
"PI_RADAR_MANAGED_LAUNCH_ID",
|
|
664
712
|
"PI_RADAR_MANAGED_EXECUTION_GENERATION",
|
|
713
|
+
"PI_RADAR_MANAGED_PARENT_SUBJECT_ID",
|
|
665
714
|
]);
|
|
666
715
|
|
|
667
716
|
/**
|
|
@@ -1015,6 +1064,12 @@ export type RadarClient = {
|
|
|
1015
1064
|
agentGet(agent_id: string): Promise<RadarResult<RadarAgentProcess>>;
|
|
1016
1065
|
observe(): Promise<RadarResult<RadarInventory>>;
|
|
1017
1066
|
/** Read the durable event journal after `after`, waiting at most `wait_ms`. */
|
|
1067
|
+
communicationPublish(request: HandoffPublishParamsDeserialize & {
|
|
1068
|
+
request_id: string;
|
|
1069
|
+
}): Promise<RadarResult<{ handoff: HandoffSerialize; created: boolean }>>;
|
|
1070
|
+
communicationInbox(request: { agent_id: string }): Promise<RadarResult<HandoffInboxSerialize>>;
|
|
1071
|
+
communicationGet(request: { agent_id: string; handoff_id: string }): Promise<RadarResult<{ handoff: HandoffSerialize; receipt: HandoffReceiptSerialize | null }>>;
|
|
1072
|
+
communicationAck(request: { agent_id: string; handoff_id: string }): Promise<RadarResult<{ receipt: HandoffReceiptSerialize; created: boolean }>>;
|
|
1018
1073
|
events(request: {
|
|
1019
1074
|
after: number;
|
|
1020
1075
|
epoch?: string;
|
|
@@ -1666,6 +1721,56 @@ export function createRadarClient(options: RadarClientOptions = {}): RadarClient
|
|
|
1666
1721
|
return protocol("spawn.close reply carries no close outcome");
|
|
1667
1722
|
});
|
|
1668
1723
|
},
|
|
1724
|
+
async communicationPublish(request) {
|
|
1725
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
1726
|
+
const answer = await call("communication.publish", {
|
|
1727
|
+
sender_agent_id: request.sender_agent_id,
|
|
1728
|
+
recipient_subject_id: request.recipient_subject_id,
|
|
1729
|
+
...(request.correlation_id !== undefined ? { correlation_id: request.correlation_id } : {}),
|
|
1730
|
+
summary: request.summary,
|
|
1731
|
+
content: request.content,
|
|
1732
|
+
}, request.request_id);
|
|
1733
|
+
if (answer.ok === false) return answer;
|
|
1734
|
+
const handoff = handoffAt(recordAt(answer.value, "handoff"));
|
|
1735
|
+
const created = recordAt(answer.value, "created");
|
|
1736
|
+
if (handoff === undefined || typeof created !== "boolean")
|
|
1737
|
+
return protocol("communication.publish reply carries no handoff");
|
|
1738
|
+
return { ok: true, value: { handoff, created } };
|
|
1739
|
+
});
|
|
1740
|
+
},
|
|
1741
|
+
async communicationInbox(request) {
|
|
1742
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
1743
|
+
const answer = await call("communication.inbox", request);
|
|
1744
|
+
if (answer.ok === false) return answer;
|
|
1745
|
+
const handoffIds = recordAt(answer.value, "handoff_ids");
|
|
1746
|
+
if (!Array.isArray(handoffIds) || handoffIds.some((id) => !isUuid(id)))
|
|
1747
|
+
return protocol("communication.inbox reply carries malformed handoff IDs");
|
|
1748
|
+
return { ok: true, value: answer.value as HandoffInboxSerialize };
|
|
1749
|
+
});
|
|
1750
|
+
},
|
|
1751
|
+
async communicationGet(request) {
|
|
1752
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
1753
|
+
const answer = await call("communication.get", request);
|
|
1754
|
+
if (answer.ok === false) return answer;
|
|
1755
|
+
const handoff = handoffAt(recordAt(answer.value, "handoff"));
|
|
1756
|
+
const receiptValue = recordAt(answer.value, "receipt");
|
|
1757
|
+
const receipt = receiptValue === null ? null : handoffReceiptAt(receiptValue);
|
|
1758
|
+
if (handoff === undefined || (receiptValue !== null && receipt === undefined))
|
|
1759
|
+
return protocol("communication.get reply carries malformed content or receipt");
|
|
1760
|
+
return { ok: true, value: { handoff, receipt: receipt ?? null } };
|
|
1761
|
+
});
|
|
1762
|
+
},
|
|
1763
|
+
async communicationAck(request) {
|
|
1764
|
+
return withContract(RADAR_COMMUNICATION_CONTRACT, async () => {
|
|
1765
|
+
const answer = await call("communication.ack", request);
|
|
1766
|
+
if (answer.ok === false) return answer;
|
|
1767
|
+
const receipt = handoffReceiptAt(recordAt(answer.value, "receipt"));
|
|
1768
|
+
const created = recordAt(answer.value, "created");
|
|
1769
|
+
if (receipt === undefined || typeof created !== "boolean")
|
|
1770
|
+
return protocol("communication.ack reply carries no receipt");
|
|
1771
|
+
return { ok: true, value: { receipt, created } };
|
|
1772
|
+
});
|
|
1773
|
+
},
|
|
1669
1774
|
async events(request) {
|
|
1670
1775
|
if (
|
|
1671
1776
|
!Number.isSafeInteger(request.after) ||
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { HandoffInboxSerialize, HandoffSerialize, HandoffReceiptSerialize } from "./generated-contracts.ts";
|
|
2
|
+
import type { RadarClient, RadarResult } from "./radar-client.ts";
|
|
3
|
+
import type { RadarPublication } from "./radar-publication.ts";
|
|
4
|
+
|
|
5
|
+
export type RadarHandoffPort = {
|
|
6
|
+
/** Publish to this managed execution's Radar-recorded parent. Retry an uncertain result with the same ID and bytes. */
|
|
7
|
+
publish(request: {
|
|
8
|
+
request_id: string;
|
|
9
|
+
correlation_id?: string;
|
|
10
|
+
summary: string;
|
|
11
|
+
content: string;
|
|
12
|
+
}): Promise<RadarResult<{ handoff: HandoffSerialize; created: boolean }>>;
|
|
13
|
+
/** Durable inbox scan for this process's managed subject. Notifications are hints; callers can always rescan. */
|
|
14
|
+
inbox(): Promise<RadarResult<HandoffInboxSerialize>>;
|
|
15
|
+
/** Retrieve the full immutable content even after it has been acknowledged. */
|
|
16
|
+
get(handoff_id: string): Promise<RadarResult<{ handoff: HandoffSerialize; receipt: HandoffReceiptSerialize | null }>>;
|
|
17
|
+
/** Call only after the consumer durably recorded the retrieved payload. */
|
|
18
|
+
ack(handoff_id: string): Promise<RadarResult<{ receipt: HandoffReceiptSerialize; created: boolean }>>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Bind handoff operations to this Pi process's Radar registration and the
|
|
23
|
+
* parent subject injected from its accepted managed-launch metadata.
|
|
24
|
+
*/
|
|
25
|
+
export function createRadarHandoffPort(
|
|
26
|
+
client: RadarClient,
|
|
27
|
+
publication: Pick<RadarPublication, "subject">,
|
|
28
|
+
parentSubjectId: () => string | undefined = () => process.env.PI_RADAR_MANAGED_PARENT_SUBJECT_ID,
|
|
29
|
+
): RadarHandoffPort {
|
|
30
|
+
async function agentId(): Promise<RadarResult<string>> {
|
|
31
|
+
const subject = await publication.subject();
|
|
32
|
+
return subject === undefined
|
|
33
|
+
? { ok: false, code: "absent", message: "this process has no current Radar registration" }
|
|
34
|
+
: { ok: true, value: subject.agent_id };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
async publish(request) {
|
|
39
|
+
const identity = await agentId();
|
|
40
|
+
if (identity.ok === false) return identity;
|
|
41
|
+
const parent = parentSubjectId();
|
|
42
|
+
if (parent === undefined)
|
|
43
|
+
return { ok: false, code: "absent", message: "this managed execution has no Radar parent subject" };
|
|
44
|
+
return client.communicationPublish({
|
|
45
|
+
...request,
|
|
46
|
+
sender_agent_id: identity.value,
|
|
47
|
+
recipient_subject_id: parent,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async inbox() {
|
|
51
|
+
const identity = await agentId();
|
|
52
|
+
if (identity.ok === false) return identity;
|
|
53
|
+
return client.communicationInbox({ agent_id: identity.value });
|
|
54
|
+
},
|
|
55
|
+
async get(handoff_id) {
|
|
56
|
+
const identity = await agentId();
|
|
57
|
+
if (identity.ok === false) return identity;
|
|
58
|
+
return client.communicationGet({ agent_id: identity.value, handoff_id });
|
|
59
|
+
},
|
|
60
|
+
async ack(handoff_id) {
|
|
61
|
+
const identity = await agentId();
|
|
62
|
+
if (identity.ok === false) return identity;
|
|
63
|
+
return client.communicationAck({ agent_id: identity.value, handoff_id });
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -64,9 +64,11 @@ export type LaunchMailbox<State extends LaunchState> = {
|
|
|
64
64
|
* different leaves the launch unresolved rather than placed.
|
|
65
65
|
*/
|
|
66
66
|
|
|
67
|
-
/** The exact parent this launch may use, and nothing inferred about it. */
|
|
67
|
+
/** The exact managed parent this launch may use, and nothing inferred about it. */
|
|
68
68
|
export type RadarLaunchParent = {
|
|
69
69
|
agent_id: string;
|
|
70
|
+
/** Stable subject from this process's current Radar registration. */
|
|
71
|
+
subject_id: string;
|
|
70
72
|
location?: RadarLocation;
|
|
71
73
|
};
|
|
72
74
|
|
|
@@ -395,6 +397,10 @@ export async function launchRadarWorker<State extends LaunchState>(
|
|
|
395
397
|
return refusedLaunch(
|
|
396
398
|
"the launch has no exact parent registration to spawn under",
|
|
397
399
|
);
|
|
400
|
+
if (!isUuid(launch.parent.subject_id))
|
|
401
|
+
return refusedLaunch(
|
|
402
|
+
"the launch has no managed Radar subject for its parent",
|
|
403
|
+
);
|
|
398
404
|
// A spawn is created under the pane its parent registered, and this port
|
|
399
405
|
// registers no location it was not given: without one there is nothing to
|
|
400
406
|
// place the child beside, and a name or session lookup is not a substitute.
|
|
@@ -408,6 +414,7 @@ export async function launchRadarWorker<State extends LaunchState>(
|
|
|
408
414
|
parent: launch.parent.agent_id,
|
|
409
415
|
parent_session_id: launch.parent_session_id,
|
|
410
416
|
executable: launch.executable,
|
|
417
|
+
managed: { parent_subject_id: launch.parent.subject_id },
|
|
411
418
|
argv: launch.argv,
|
|
412
419
|
...(launch.cwd !== "" ? { cwd: launch.cwd } : {}),
|
|
413
420
|
...(Object.keys(launch.env).length > 0 ? { env: launch.env } : {}),
|
|
@@ -1318,6 +1318,8 @@ export type RadarPublication = {
|
|
|
1318
1318
|
/** What a process's own registration resolved to, and where it says it is. */
|
|
1319
1319
|
export type RadarSubject = {
|
|
1320
1320
|
agent_id: string;
|
|
1321
|
+
/** Stable managed logical subject, present only for a Radar-issued launch. */
|
|
1322
|
+
subject_id?: string;
|
|
1321
1323
|
location?: RadarLocation;
|
|
1322
1324
|
};
|
|
1323
1325
|
|
|
@@ -1716,8 +1718,10 @@ function createRadarPublicationInner(
|
|
|
1716
1718
|
const record = await ensureRegistration();
|
|
1717
1719
|
if (record === undefined) return undefined;
|
|
1718
1720
|
const location = record.registration.location;
|
|
1721
|
+
const subjectId = process.env.PI_RADAR_MANAGED_SUBJECT_ID;
|
|
1719
1722
|
return {
|
|
1720
1723
|
agent_id: agentId,
|
|
1724
|
+
...(subjectId !== undefined ? { subject_id: subjectId } : {}),
|
|
1721
1725
|
...(location !== undefined ? { location } : {}),
|
|
1722
1726
|
};
|
|
1723
1727
|
},
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { RadarExecutionAdapter } from "./radar-execution.ts";
|
|
2
|
+
import type { RadarHandoffPort } from "./radar-communication.ts";
|
|
2
3
|
import type { RadarPublication } from "./radar-publication.ts";
|
|
3
4
|
|
|
4
5
|
/** The event channel through which Pi extensions obtain the baseline runtime. */
|
|
5
6
|
export const RADAR_RUNTIME_REQUEST = "pi-radar/runtime-request";
|
|
6
|
-
export const RADAR_RUNTIME_VERSION =
|
|
7
|
+
export const RADAR_RUNTIME_VERSION = 2 as const;
|
|
7
8
|
|
|
8
9
|
/** The sole baseline publisher's session-scoped runtime, shared with consumers. */
|
|
9
10
|
export type RadarRuntimeServices = {
|
|
10
11
|
/** The one baseline publication; Herdsman contributes through its channels. */
|
|
11
12
|
publication: RadarPublication;
|
|
13
|
+
/** Durable handoffs bound to this process's registration and managed parent. */
|
|
14
|
+
communication: RadarHandoffPort;
|
|
12
15
|
execution: RadarExecutionAdapter;
|
|
13
16
|
};
|
|
14
17
|
|
package/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * as client from "./extension/radar-client.ts";
|
|
2
|
+
export * as communication from "./extension/radar-communication.ts";
|
|
2
3
|
export * as execution from "./extension/radar-execution.ts";
|
|
3
4
|
export * as inspection from "./extension/radar-inspection.ts";
|
|
4
5
|
export * as launch from "./extension/radar-launch.ts";
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-radar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Radar daemon client, publication, execution, location, launch and inspection surface for Pi extensions.",
|
|
5
|
+
"description": "Radar daemon client, communication, publication, execution, location, launch and inspection surface for Pi extensions.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22.19.0"
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"types": "./extension/radar-client.ts",
|
|
26
26
|
"import": "./dist/extension/radar-client.js"
|
|
27
27
|
},
|
|
28
|
+
"./communication": {
|
|
29
|
+
"types": "./extension/radar-communication.ts",
|
|
30
|
+
"import": "./dist/extension/radar-communication.js"
|
|
31
|
+
},
|
|
28
32
|
"./execution": {
|
|
29
33
|
"types": "./extension/radar-execution.ts",
|
|
30
34
|
"import": "./dist/extension/radar-execution.js"
|
|
@@ -67,6 +71,7 @@
|
|
|
67
71
|
"schema/public/v1/**",
|
|
68
72
|
"extension/generated-contracts.ts",
|
|
69
73
|
"extension/radar-client.ts",
|
|
74
|
+
"extension/radar-communication.ts",
|
|
70
75
|
"extension/radar-execution.ts",
|
|
71
76
|
"extension/radar-inspection.ts",
|
|
72
77
|
"extension/radar-launch.ts",
|
|
@@ -77,10 +82,10 @@
|
|
|
77
82
|
"extension/baseline.ts"
|
|
78
83
|
],
|
|
79
84
|
"scripts": {
|
|
80
|
-
"build": "esbuild index.ts extension/generated-contracts.ts extension/radar-client.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-runtime.ts extension/baseline.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
|
|
85
|
+
"build": "esbuild index.ts extension/generated-contracts.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-inspection.ts extension/radar-launch.ts extension/radar-location.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-runtime.ts extension/baseline.ts --outbase=. --outdir=dist --bundle --platform=node --format=esm --target=node22",
|
|
81
86
|
"generate:contracts": "node tools/generate-contracts.mjs",
|
|
82
87
|
"check:contracts": "node tools/generate-contracts.mjs --check",
|
|
83
|
-
"typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node index.ts extension/baseline.ts extension/generated-contracts.ts extension/generated-contracts.typecheck.ts extension/radar-client.ts extension/radar-execution.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-session-marker.test.ts extension/radar-runtime.ts extension/radar-runtime.test.ts",
|
|
88
|
+
"typecheck:contracts": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext --allowImportingTsExtensions --types node index.ts extension/baseline.ts extension/generated-contracts.ts extension/generated-contracts.typecheck.ts extension/radar-client.ts extension/radar-communication.ts extension/radar-execution.ts extension/radar-launch.test.ts extension/radar-publication.ts extension/radar-session-marker.ts extension/radar-session-marker.test.ts extension/radar-runtime.ts extension/radar-runtime.test.ts",
|
|
84
89
|
"prepack": "npm run build",
|
|
85
90
|
"test": "npm run check:contracts && npm run typecheck:contracts && node --experimental-strip-types --test extension/*.test.ts"
|
|
86
91
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"description": "A bounded inbox page by stable handoff ID. Payloads are fetched one at a\ntime with `communication.get`, keeping each response within the wire bound.",
|
|
5
|
+
"properties": {
|
|
6
|
+
"handoff_ids": {
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"type": "array"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"required": [
|
|
14
|
+
"handoff_ids"
|
|
15
|
+
],
|
|
16
|
+
"title": "handoff-inbox-deserialize",
|
|
17
|
+
"type": "object"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"description": "A bounded inbox page by stable handoff ID. Payloads are fetched one at a\ntime with `communication.get`, keeping each response within the wire bound.",
|
|
5
|
+
"properties": {
|
|
6
|
+
"handoff_ids": {
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"type": "array"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"required": [
|
|
14
|
+
"handoff_ids"
|
|
15
|
+
],
|
|
16
|
+
"title": "handoff-inbox-serialize",
|
|
17
|
+
"type": "object"
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"description": "Inputs for publishing an immutable handoff. The wire request ID carries the\nstable communication identity; the daemon derives the sender execution.",
|
|
5
|
+
"properties": {
|
|
6
|
+
"content": {
|
|
7
|
+
"type": "string"
|
|
8
|
+
},
|
|
9
|
+
"correlation_id": {
|
|
10
|
+
"type": [
|
|
11
|
+
"string",
|
|
12
|
+
"null"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"recipient_subject_id": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"sender_agent_id": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
"summary": {
|
|
22
|
+
"type": "string"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": [
|
|
26
|
+
"sender_agent_id",
|
|
27
|
+
"recipient_subject_id",
|
|
28
|
+
"summary",
|
|
29
|
+
"content"
|
|
30
|
+
],
|
|
31
|
+
"title": "handoff-publish-params-deserialize",
|
|
32
|
+
"type": "object"
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"description": "Inputs for publishing an immutable handoff. The wire request ID carries the\nstable communication identity; the daemon derives the sender execution.",
|
|
5
|
+
"properties": {
|
|
6
|
+
"content": {
|
|
7
|
+
"type": "string"
|
|
8
|
+
},
|
|
9
|
+
"correlation_id": {
|
|
10
|
+
"type": [
|
|
11
|
+
"string",
|
|
12
|
+
"null"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"recipient_subject_id": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"sender_agent_id": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
"summary": {
|
|
22
|
+
"type": "string"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": [
|
|
26
|
+
"sender_agent_id",
|
|
27
|
+
"recipient_subject_id",
|
|
28
|
+
"summary",
|
|
29
|
+
"content"
|
|
30
|
+
],
|
|
31
|
+
"title": "handoff-publish-params-serialize",
|
|
32
|
+
"type": "object"
|
|
33
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$defs": {
|
|
3
|
+
"HandoffIdentity": {
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"description": "The current managed Pi execution that published or received a handoff.\n`subject_id` is stable across verified successors; `execution_id` and\ngeneration identify the exact execution that took the action.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"agent_id": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"execution_generation": {
|
|
11
|
+
"format": "uint32",
|
|
12
|
+
"maximum": 4294967295,
|
|
13
|
+
"minimum": 1,
|
|
14
|
+
"type": "integer"
|
|
15
|
+
},
|
|
16
|
+
"execution_id": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"session_id": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"subject_id": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"required": [
|
|
27
|
+
"agent_id",
|
|
28
|
+
"session_id",
|
|
29
|
+
"subject_id",
|
|
30
|
+
"execution_id",
|
|
31
|
+
"execution_generation"
|
|
32
|
+
],
|
|
33
|
+
"type": "object"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"description": "Durable recipient-consumer receipt, stored separately from immutable content.",
|
|
39
|
+
"properties": {
|
|
40
|
+
"handoff_id": {
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"received_at_unix_ms": {
|
|
44
|
+
"format": "uint64",
|
|
45
|
+
"maximum": 9007199254740991,
|
|
46
|
+
"minimum": 0,
|
|
47
|
+
"type": "integer"
|
|
48
|
+
},
|
|
49
|
+
"recipient": {
|
|
50
|
+
"$ref": "#/$defs/HandoffIdentity"
|
|
51
|
+
},
|
|
52
|
+
"version": {
|
|
53
|
+
"format": "uint32",
|
|
54
|
+
"minimum": 0,
|
|
55
|
+
"type": "integer"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"version",
|
|
60
|
+
"handoff_id",
|
|
61
|
+
"recipient",
|
|
62
|
+
"received_at_unix_ms"
|
|
63
|
+
],
|
|
64
|
+
"title": "handoff-receipt-deserialize",
|
|
65
|
+
"type": "object"
|
|
66
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$defs": {
|
|
3
|
+
"HandoffIdentity": {
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"description": "The current managed Pi execution that published or received a handoff.\n`subject_id` is stable across verified successors; `execution_id` and\ngeneration identify the exact execution that took the action.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"agent_id": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"execution_generation": {
|
|
11
|
+
"format": "uint32",
|
|
12
|
+
"maximum": 4294967295,
|
|
13
|
+
"minimum": 1,
|
|
14
|
+
"type": "integer"
|
|
15
|
+
},
|
|
16
|
+
"execution_id": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"session_id": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"subject_id": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"required": [
|
|
27
|
+
"agent_id",
|
|
28
|
+
"session_id",
|
|
29
|
+
"subject_id",
|
|
30
|
+
"execution_id",
|
|
31
|
+
"execution_generation"
|
|
32
|
+
],
|
|
33
|
+
"type": "object"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"description": "Durable recipient-consumer receipt, stored separately from immutable content.",
|
|
39
|
+
"properties": {
|
|
40
|
+
"handoff_id": {
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"received_at_unix_ms": {
|
|
44
|
+
"format": "uint64",
|
|
45
|
+
"maximum": 9007199254740991,
|
|
46
|
+
"minimum": 0,
|
|
47
|
+
"type": "integer"
|
|
48
|
+
},
|
|
49
|
+
"recipient": {
|
|
50
|
+
"$ref": "#/$defs/HandoffIdentity"
|
|
51
|
+
},
|
|
52
|
+
"version": {
|
|
53
|
+
"format": "uint32",
|
|
54
|
+
"minimum": 0,
|
|
55
|
+
"type": "integer"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": [
|
|
59
|
+
"version",
|
|
60
|
+
"handoff_id",
|
|
61
|
+
"recipient",
|
|
62
|
+
"received_at_unix_ms"
|
|
63
|
+
],
|
|
64
|
+
"title": "handoff-receipt-serialize",
|
|
65
|
+
"type": "object"
|
|
66
|
+
}
|