@ctliz/agent-intercom-pi 0.11.0-connect.2

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.
@@ -0,0 +1,87 @@
1
+ import type { Socket } from "net";
2
+
3
+ export const MAX_FRAME_BYTES = 1024 * 1024;
4
+
5
+ /**
6
+ * Write a length-prefixed message to a socket.
7
+ * Format: 4-byte big-endian length + JSON payload
8
+ */
9
+ export function writeMessage(socket: Socket, msg: unknown): void {
10
+ const json = JSON.stringify(msg);
11
+ const payload = Buffer.from(json, "utf-8");
12
+ const header = Buffer.alloc(4);
13
+ header.writeUInt32BE(payload.length, 0);
14
+ socket.write(Buffer.concat([header, payload]));
15
+ }
16
+
17
+ /**
18
+ * Create a message reader that handles partial reads.
19
+ * Calls onMessage for each complete message received.
20
+ * Protocol or handler errors are reported to onError so the caller can close the socket.
21
+ */
22
+ export function createMessageReader(
23
+ onMessage: (msg: unknown) => void,
24
+ onError: (error: Error) => void,
25
+ maxFrameBytes = MAX_FRAME_BYTES,
26
+ ) {
27
+ let buffer = Buffer.alloc(0);
28
+
29
+ function reportMessage(payload: Buffer): boolean {
30
+ let msg: unknown;
31
+ try {
32
+ msg = JSON.parse(payload.toString("utf-8"));
33
+ } catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ onError(new Error(`Failed to parse intercom message: ${message}`, { cause: error }));
36
+ return false;
37
+ }
38
+
39
+ try {
40
+ onMessage(msg);
41
+ return true;
42
+ } catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ onError(new Error(`Failed to handle intercom message: ${message}`, { cause: error }));
45
+ return false;
46
+ }
47
+ }
48
+
49
+ return (data: Buffer) => {
50
+ let remaining = data;
51
+
52
+ while (remaining.length > 0) {
53
+ if (buffer.length < 4) {
54
+ const headerBytes = Math.min(4 - buffer.length, remaining.length);
55
+ buffer = Buffer.concat([buffer, remaining.subarray(0, headerBytes)]);
56
+ remaining = remaining.subarray(headerBytes);
57
+ if (buffer.length < 4) {
58
+ return;
59
+ }
60
+ }
61
+
62
+ const length = buffer.readUInt32BE(0);
63
+ if (length > maxFrameBytes) {
64
+ buffer = Buffer.alloc(0);
65
+ onError(new Error(`Intercom frame length ${length} exceeds maximum ${maxFrameBytes} bytes`));
66
+ return;
67
+ }
68
+
69
+ const missingPayloadBytes = length - Math.max(0, buffer.length - 4);
70
+ const payloadBytes = Math.min(missingPayloadBytes, remaining.length);
71
+ if (payloadBytes > 0) {
72
+ buffer = Buffer.concat([buffer, remaining.subarray(0, payloadBytes)]);
73
+ remaining = remaining.subarray(payloadBytes);
74
+ }
75
+
76
+ if (buffer.length < 4 + length) {
77
+ return;
78
+ }
79
+
80
+ const payload = buffer.subarray(4, 4 + length);
81
+ buffer = Buffer.alloc(0);
82
+ if (!reportMessage(payload)) {
83
+ return;
84
+ }
85
+ }
86
+ };
87
+ }
@@ -0,0 +1,61 @@
1
+ import { closeSync, constants, openSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { INTERCOM_RUNTIME_FILE_MODE, restrictIntercomRuntimeFile } from "./paths.ts";
3
+
4
+ function ownerPid(path: string): number | null {
5
+ try {
6
+ const pid = Number.parseInt(readFileSync(path, "utf8").trim(), 10);
7
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+
13
+ function pidIsAlive(pid: number): boolean {
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ } catch (error) {
18
+ return (error as NodeJS.ErrnoException).code !== "ESRCH";
19
+ }
20
+ }
21
+
22
+ export function acquireBrokerOwnership(path: string, pid = process.pid): void {
23
+ for (let attempt = 0; attempt < 2; attempt += 1) {
24
+ try {
25
+ const fd = openSync(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, INTERCOM_RUNTIME_FILE_MODE);
26
+ try {
27
+ writeFileSync(fd, String(pid));
28
+ } finally {
29
+ closeSync(fd);
30
+ }
31
+ restrictIntercomRuntimeFile(path);
32
+ return;
33
+ } catch (error) {
34
+ if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
35
+ const existingPid = ownerPid(path);
36
+ if (existingPid !== null && pidIsAlive(existingPid)) {
37
+ throw new Error(`Intercom broker already owned by live process ${existingPid}`);
38
+ }
39
+ try {
40
+ unlinkSync(path);
41
+ } catch (unlinkError) {
42
+ if ((unlinkError as NodeJS.ErrnoException).code !== "ENOENT") throw unlinkError;
43
+ }
44
+ }
45
+ }
46
+ throw new Error("Could not acquire intercom broker ownership");
47
+ }
48
+
49
+ export function releaseBrokerOwnership(path: string, pid = process.pid): void {
50
+ if (ownerPid(path) !== pid) return;
51
+ try {
52
+ unlinkSync(path);
53
+ } catch (error) {
54
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
55
+ }
56
+ }
57
+
58
+ export function hasBrokerOwnership(path: string, pid = process.pid): boolean {
59
+ return ownerPid(path) === pid;
60
+ }
61
+
@@ -0,0 +1,161 @@
1
+ import { chmodSync, mkdirSync, readFileSync } from "fs";
2
+ import { isAbsolute, join, resolve } from "path";
3
+ import { homedir } from "os";
4
+
5
+ export const INTERCOM_DIR_MODE = 0o700;
6
+ export const INTERCOM_RUNTIME_FILE_MODE = 0o600;
7
+ export const INTERCOM_TCP_HOST = "127.0.0.1";
8
+ export const INTERCOM_PROTOCOL_NAME = "pi-intercom";
9
+ export const INTERCOM_PROTOCOL_VERSION = 4;
10
+
11
+ export interface BrokerTcpEndpoint {
12
+ transport: "tcp";
13
+ host: string;
14
+ port: number;
15
+ stateId?: string;
16
+ }
17
+
18
+ export type BrokerConnectTarget = string | BrokerTcpEndpoint;
19
+
20
+ function sanitizePipeSegment(value: string): string {
21
+ return value
22
+ .replace(/[^a-zA-Z0-9]+/g, "-")
23
+ .replace(/^-+|-+$/g, "")
24
+ .toLowerCase() || "default";
25
+ }
26
+
27
+ export function getAgentDirPath(
28
+ env: NodeJS.ProcessEnv = process.env,
29
+ homeDir: string = homedir(),
30
+ cwd: string = process.cwd(),
31
+ ): string {
32
+ const configured = env.PI_CODING_AGENT_DIR?.trim();
33
+ if (!configured) {
34
+ return join(homeDir, ".pi/agent");
35
+ }
36
+
37
+ return isAbsolute(configured) ? configured : resolve(cwd, configured);
38
+ }
39
+
40
+ export function getIntercomDirPath(agentDir: string = getAgentDirPath()): string {
41
+ return join(agentDir, "intercom");
42
+ }
43
+
44
+ export function shouldUseWindowsTcpTransport(
45
+ platform: NodeJS.Platform = process.platform,
46
+ env: NodeJS.ProcessEnv = process.env,
47
+ ): boolean {
48
+ if (platform !== "win32") {
49
+ return false;
50
+ }
51
+
52
+ const transport = env.PI_INTERCOM_TRANSPORT?.trim().toLowerCase();
53
+ if (transport === "tcp") {
54
+ return true;
55
+ }
56
+
57
+ const legacyOptIn = env.PI_INTERCOM_TCP?.trim().toLowerCase();
58
+ return legacyOptIn === "1" || legacyOptIn === "true";
59
+ }
60
+
61
+ export function getBrokerPortFilePath(intercomDir: string = getIntercomDirPath()): string {
62
+ return join(intercomDir, "broker.port.json");
63
+ }
64
+
65
+ export function getBrokerAskStateFilePath(intercomDir: string = getIntercomDirPath()): string {
66
+ return join(intercomDir, "broker-asks.json");
67
+ }
68
+
69
+ export function getBrokerAccessStateFilePath(intercomDir: string = getIntercomDirPath()): string {
70
+ return join(intercomDir, "broker-access.json");
71
+ }
72
+
73
+ export function getBrokerAdminCredentialFilePath(intercomDir: string = getIntercomDirPath()): string {
74
+ return join(intercomDir, "broker-admin.json");
75
+ }
76
+
77
+ export function getBrokerAuditFilePath(intercomDir: string = getIntercomDirPath()): string {
78
+ return join(intercomDir, "broker-audit.jsonl");
79
+ }
80
+
81
+ export function getRemoteGatewaySocketPath(
82
+ platform: NodeJS.Platform = process.platform,
83
+ agentDir: string = getAgentDirPath(),
84
+ ): string {
85
+ if (platform === "win32") {
86
+ return `\\\\.\\pipe\\pi-intercom-remote-${sanitizePipeSegment(agentDir)}`;
87
+ }
88
+ return join(getIntercomDirPath(agentDir), "remote-gateway.sock");
89
+ }
90
+
91
+ export function getBrokerSocketPath(
92
+ platform: NodeJS.Platform = process.platform,
93
+ agentDir: string = getAgentDirPath(),
94
+ ): string {
95
+ if (platform === "win32") {
96
+ return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
97
+ }
98
+
99
+ return join(getIntercomDirPath(agentDir), "broker.sock");
100
+ }
101
+
102
+ export function getBrokerConnectTarget(
103
+ platform: NodeJS.Platform = process.platform,
104
+ env: NodeJS.ProcessEnv = process.env,
105
+ intercomDir: string = getIntercomDirPath(getAgentDirPath(env)),
106
+ ): BrokerConnectTarget {
107
+ if (shouldUseWindowsTcpTransport(platform, env)) {
108
+ const endpointFile = getBrokerPortFilePath(intercomDir);
109
+ const raw = readFileSync(endpointFile, "utf-8");
110
+ const parsed: unknown = JSON.parse(raw);
111
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
112
+ throw new Error(`Invalid intercom TCP endpoint at ${endpointFile}: expected a JSON object`);
113
+ }
114
+ const endpoint = parsed as Record<string, unknown>;
115
+ if (
116
+ endpoint.transport !== "tcp"
117
+ || endpoint.host !== INTERCOM_TCP_HOST
118
+ || typeof endpoint.port !== "number"
119
+ || !Number.isSafeInteger(endpoint.port)
120
+ || endpoint.port <= 0
121
+ || endpoint.port > 65535
122
+ || typeof endpoint.stateId !== "string"
123
+ || endpoint.stateId.length === 0
124
+ ) {
125
+ throw new Error(`Invalid intercom TCP endpoint at ${endpointFile}`);
126
+ }
127
+ return { transport: "tcp", host: endpoint.host, port: endpoint.port, stateId: endpoint.stateId };
128
+ }
129
+
130
+ return getBrokerSocketPath(platform, getAgentDirPath(env));
131
+ }
132
+
133
+ export function getBrokerListenTarget(
134
+ platform: NodeJS.Platform = process.platform,
135
+ env: NodeJS.ProcessEnv = process.env,
136
+ ): BrokerConnectTarget {
137
+ if (shouldUseWindowsTcpTransport(platform, env)) {
138
+ return { transport: "tcp", host: INTERCOM_TCP_HOST, port: 0 };
139
+ }
140
+
141
+ return getBrokerSocketPath(platform, getAgentDirPath(env));
142
+ }
143
+
144
+ export function ensureIntercomRuntimeDir(
145
+ intercomDir: string = getIntercomDirPath(),
146
+ platform: NodeJS.Platform = process.platform,
147
+ ): void {
148
+ mkdirSync(intercomDir, { recursive: true, mode: INTERCOM_DIR_MODE });
149
+ if (platform !== "win32") {
150
+ chmodSync(intercomDir, INTERCOM_DIR_MODE);
151
+ }
152
+ }
153
+
154
+ export function restrictIntercomRuntimeFile(
155
+ filePath: string,
156
+ platform: NodeJS.Platform = process.platform,
157
+ ): void {
158
+ if (platform !== "win32") {
159
+ chmodSync(filePath, INTERCOM_RUNTIME_FILE_MODE);
160
+ }
161
+ }
@@ -0,0 +1,158 @@
1
+ import { types as nodeUtilTypes } from "node:util";
2
+ import { BROKER_PROTECTED_PROVIDER_ROOT } from "@ctliz/agent-intercom-core/boss";
3
+
4
+ export const PI_BOSS_PROTECTED_PROVIDER_ID = "pi" as const;
5
+ export const PI_BOSS_PROTECTED_PROVIDER_PACKAGE = "@ctliz/agent-intercom-pi" as const;
6
+ export const PI_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH =
7
+ `${BROKER_PROTECTED_PROVIDER_ROOT}${PI_BOSS_PROTECTED_PROVIDER_ID}/provider.mjs` as const;
8
+ export const BOSS_PROTECTED_SERVICE_UNAVAILABLE = "BOSS_PROTECTED_SERVICE_UNAVAILABLE" as const;
9
+
10
+ const PI_BOSS_PROTECTED_PROVIDER_MODE = "0555" as const;
11
+ const CANONICAL_SEMANTIC_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
12
+ const CANDIDATE_KEYS = [
13
+ "adapterId",
14
+ "providerPackage",
15
+ "providerVersion",
16
+ "providerDigest",
17
+ "artifactPath",
18
+ "artifactOwnerUid",
19
+ "artifactOwnerGid",
20
+ "artifactMode",
21
+ ] as const;
22
+
23
+ export interface PiBossProtectedProviderArtifactCandidate {
24
+ adapterId: typeof PI_BOSS_PROTECTED_PROVIDER_ID;
25
+ providerPackage: typeof PI_BOSS_PROTECTED_PROVIDER_PACKAGE;
26
+ providerVersion: string;
27
+ providerDigest: string;
28
+ artifactPath: typeof PI_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH;
29
+ artifactOwnerUid: 0;
30
+ artifactOwnerGid: 0;
31
+ artifactMode: typeof PI_BOSS_PROTECTED_PROVIDER_MODE;
32
+ }
33
+
34
+ export type PiBossProtectedServiceErrorCode =
35
+ | "INVALID_PI_PROTECTED_PROVIDER_CANDIDATE"
36
+ | typeof BOSS_PROTECTED_SERVICE_UNAVAILABLE;
37
+
38
+ export class PiBossProtectedServiceError extends Error {
39
+ readonly code: PiBossProtectedServiceErrorCode;
40
+ readonly path: string;
41
+
42
+ constructor(code: PiBossProtectedServiceErrorCode, path: string, message: string) {
43
+ super(`${path}: ${message}`);
44
+ this.name = "PiBossProtectedServiceError";
45
+ this.code = code;
46
+ this.path = path;
47
+ }
48
+ }
49
+
50
+ function invalid(path: string, message: string): never {
51
+ throw new PiBossProtectedServiceError("INVALID_PI_PROTECTED_PROVIDER_CANDIDATE", path, message);
52
+ }
53
+
54
+ function assertExactOwnDataCandidate(value: unknown): asserts value is Record<string, unknown> {
55
+ const path = "$candidate";
56
+ if (
57
+ typeof value !== "object"
58
+ || value === null
59
+ || nodeUtilTypes.isProxy(value)
60
+ || Array.isArray(value)
61
+ || Object.getPrototypeOf(value) !== Object.prototype
62
+ ) {
63
+ invalid(path, "must be a non-proxy plain data object");
64
+ }
65
+
66
+ const keys = Reflect.ownKeys(value);
67
+ if (
68
+ keys.length !== CANDIDATE_KEYS.length
69
+ || keys.some((key) => typeof key !== "string" || !CANDIDATE_KEYS.includes(key as typeof CANDIDATE_KEYS[number]))
70
+ ) {
71
+ invalid(path, "must contain exactly the canonical unsigned Pi provider candidate fields");
72
+ }
73
+
74
+ for (const key of CANDIDATE_KEYS) {
75
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
76
+ if (
77
+ descriptor === undefined
78
+ || !descriptor.enumerable
79
+ || !Object.hasOwn(descriptor, "value")
80
+ ) {
81
+ invalid(`${path}.${key}`, "must be an own enumerable data property");
82
+ }
83
+ }
84
+ }
85
+
86
+ function ownValue(value: Record<string, unknown>, key: typeof CANDIDATE_KEYS[number]): unknown {
87
+ return Object.getOwnPropertyDescriptor(value, key)!.value;
88
+ }
89
+
90
+ function readProviderVersion(value: unknown): string {
91
+ if (
92
+ typeof value !== "string"
93
+ || value.length > 128
94
+ || /[\r\n\u2028\u2029]/.test(value)
95
+ || !CANONICAL_SEMANTIC_VERSION.test(value)
96
+ ) {
97
+ invalid("$candidate.providerVersion", "must be a canonical semantic version");
98
+ }
99
+ return value;
100
+ }
101
+
102
+ function readProviderDigest(value: unknown): string {
103
+ if (typeof value !== "string" || value.length !== 64 || !/^[a-f0-9]{64}$/.test(value)) {
104
+ invalid("$candidate.providerDigest", "must be a lowercase SHA-256 digest");
105
+ }
106
+ return value;
107
+ }
108
+
109
+ /**
110
+ * Normalize an unsigned release candidate for the packaged Pi provider.
111
+ * This parser cannot verify installation, signatures, service identities, or
112
+ * authority. Its frozen result remains explicitly non-authoritative.
113
+ */
114
+ export function parsePiBossProtectedProviderArtifactCandidate(
115
+ value: unknown,
116
+ ): Readonly<PiBossProtectedProviderArtifactCandidate> {
117
+ assertExactOwnDataCandidate(value);
118
+
119
+ if (ownValue(value, "adapterId") !== PI_BOSS_PROTECTED_PROVIDER_ID) {
120
+ invalid("$candidate.adapterId", "must identify the Pi provider");
121
+ }
122
+ if (ownValue(value, "providerPackage") !== PI_BOSS_PROTECTED_PROVIDER_PACKAGE) {
123
+ invalid("$candidate.providerPackage", "must identify the canonical Pi package");
124
+ }
125
+ if (ownValue(value, "artifactPath") !== PI_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH) {
126
+ invalid("$candidate.artifactPath", "must equal the canonical protected Pi provider path");
127
+ }
128
+ if (ownValue(value, "artifactOwnerUid") !== 0 || ownValue(value, "artifactOwnerGid") !== 0) {
129
+ invalid("$candidate.artifactOwnerUid", "must describe a root:root artifact");
130
+ }
131
+ if (ownValue(value, "artifactMode") !== PI_BOSS_PROTECTED_PROVIDER_MODE) {
132
+ invalid("$candidate.artifactMode", "must be read/execute-only mode 0555");
133
+ }
134
+
135
+ return Object.freeze({
136
+ adapterId: PI_BOSS_PROTECTED_PROVIDER_ID,
137
+ providerPackage: PI_BOSS_PROTECTED_PROVIDER_PACKAGE,
138
+ providerVersion: readProviderVersion(ownValue(value, "providerVersion")),
139
+ providerDigest: readProviderDigest(ownValue(value, "providerDigest")),
140
+ artifactPath: PI_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH,
141
+ artifactOwnerUid: 0,
142
+ artifactOwnerGid: 0,
143
+ artifactMode: PI_BOSS_PROTECTED_PROVIDER_MODE,
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Production ensure is intentionally unavailable until a protected
149
+ * provisioner supplies release and service identity facts outside caller data.
150
+ * The request is never inspected while that provisioner is absent.
151
+ */
152
+ export function ensurePiBossProtectedService(_request: unknown): never {
153
+ throw new PiBossProtectedServiceError(
154
+ BOSS_PROTECTED_SERVICE_UNAVAILABLE,
155
+ "$provisioner",
156
+ "the protected Pi broker service provisioner is not installed",
157
+ );
158
+ }
@@ -0,0 +1,173 @@
1
+ import { types as nodeUtilTypes } from "node:util";
2
+ import type { SessionRegistration } from "../types.ts";
3
+ import { parseBossParticipantRegistrationMetadata } from "./boss-adapter.ts";
4
+
5
+ const REQUIRED_SESSION_KEYS = ["cwd", "model", "pid", "startedAt", "lastActivity"] as const;
6
+ const OPTIONAL_SESSION_KEYS = ["name", "status", "runtimeInstanceId", "boss"] as const;
7
+
8
+ const MAX_SESSION_NAME_LENGTH = 256;
9
+ const MAX_SESSION_CWD_LENGTH = 4096;
10
+ const MAX_SESSION_MODEL_LENGTH = 512;
11
+ const MAX_SESSION_STATUS_LENGTH = 512;
12
+ const MAX_RUNTIME_INSTANCE_ID_LENGTH = 256;
13
+
14
+ /**
15
+ * Reject every proxy in an authoritative graph before reflecting on that
16
+ * proxy. Descriptor traversal deliberately never invokes getters.
17
+ */
18
+ export function assertNoProxyGraph(value: unknown): void {
19
+ const seen = new WeakSet<object>();
20
+ const visit = (candidate: unknown): void => {
21
+ if (typeof candidate !== "object" || candidate === null) return;
22
+ if (nodeUtilTypes.isProxy(candidate)) throw new Error("Proxies are not supported");
23
+ if (seen.has(candidate)) return;
24
+ seen.add(candidate);
25
+ for (const key of Reflect.ownKeys(candidate)) {
26
+ const descriptor = Object.getOwnPropertyDescriptor(candidate, key);
27
+ if (descriptor && Object.hasOwn(descriptor, "value")) visit(descriptor.value);
28
+ }
29
+ };
30
+ visit(value);
31
+ }
32
+
33
+ function isExactPlainDataRecord(
34
+ value: unknown,
35
+ requiredKeys: readonly string[],
36
+ optionalKeys: readonly string[],
37
+ ): value is Record<string, unknown> {
38
+ assertNoProxyGraph(value);
39
+ if (
40
+ typeof value !== "object"
41
+ || value === null
42
+ || Array.isArray(value)
43
+ || Object.getPrototypeOf(value) !== Object.prototype
44
+ ) return false;
45
+
46
+ const allowed = new Set([...requiredKeys, ...optionalKeys]);
47
+ const keys = Reflect.ownKeys(value);
48
+ if (keys.some((key) => typeof key !== "string" || !allowed.has(key))) return false;
49
+
50
+ for (const key of requiredKeys) {
51
+ if (!Object.hasOwn(value, key)) return false;
52
+ }
53
+ for (const key of keys) {
54
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
55
+ if (!descriptor?.enumerable || !Object.hasOwn(descriptor, "value")) return false;
56
+ }
57
+ return true;
58
+ }
59
+
60
+ function dataValue(record: Record<string, unknown>, key: string): unknown {
61
+ return Object.getOwnPropertyDescriptor(record, key)?.value;
62
+ }
63
+
64
+ /** Exact ordinary/Boss session-registration discriminant. */
65
+ export function parseSessionRegistration(value: unknown): SessionRegistration | null {
66
+ try {
67
+ if (!isExactPlainDataRecord(value, REQUIRED_SESSION_KEYS, OPTIONAL_SESSION_KEYS)) return null;
68
+
69
+ const cwd = dataValue(value, "cwd");
70
+ const model = dataValue(value, "model");
71
+ const pid = dataValue(value, "pid");
72
+ const startedAt = dataValue(value, "startedAt");
73
+ const lastActivity = dataValue(value, "lastActivity");
74
+ if (
75
+ typeof cwd !== "string"
76
+ || cwd.length === 0
77
+ || cwd.length > MAX_SESSION_CWD_LENGTH
78
+ || typeof model !== "string"
79
+ || model.length === 0
80
+ || model.length > MAX_SESSION_MODEL_LENGTH
81
+ || typeof pid !== "number"
82
+ || !Number.isFinite(pid)
83
+ || typeof startedAt !== "number"
84
+ || !Number.isFinite(startedAt)
85
+ || typeof lastActivity !== "number"
86
+ || !Number.isFinite(lastActivity)
87
+ ) return null;
88
+
89
+ const name = dataValue(value, "name");
90
+ if (name !== undefined && (typeof name !== "string" || name.length > MAX_SESSION_NAME_LENGTH)) return null;
91
+ const status = dataValue(value, "status");
92
+ if (status !== undefined && (typeof status !== "string" || status.length > MAX_SESSION_STATUS_LENGTH)) return null;
93
+ const runtimeInstanceId = dataValue(value, "runtimeInstanceId");
94
+ if (
95
+ runtimeInstanceId !== undefined
96
+ && (
97
+ typeof runtimeInstanceId !== "string"
98
+ || runtimeInstanceId.length === 0
99
+ || runtimeInstanceId.length > MAX_RUNTIME_INSTANCE_ID_LENGTH
100
+ )
101
+ ) return null;
102
+
103
+ if (Object.hasOwn(value, "boss")) {
104
+ const boss = dataValue(value, "boss");
105
+ if (boss === undefined) return null;
106
+ parseBossParticipantRegistrationMetadata(boss);
107
+ }
108
+ return value as SessionRegistration;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ /** Exact outer client register envelope, validated before nested registration data. */
115
+ export function isExactClientRegistrationEnvelope(value: unknown): value is Record<string, unknown> & {
116
+ type: "register";
117
+ protocol: unknown;
118
+ version: unknown;
119
+ session: unknown;
120
+ } {
121
+ try {
122
+ if (!isExactPlainDataRecord(
123
+ value,
124
+ ["type", "protocol", "version", "session"],
125
+ ["sessionId", "stateId", "access", "scopeId"],
126
+ )) return false;
127
+ return dataValue(value, "type") === "register";
128
+ } catch {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ /** Exact outer client register frame with validated nested session data. */
134
+ export function isExactClientRegistrationRequest(value: unknown): value is Record<string, unknown> & {
135
+ type: "register";
136
+ protocol: string;
137
+ version: number;
138
+ session: SessionRegistration;
139
+ } {
140
+ return isExactClientRegistrationEnvelope(value)
141
+ && parseSessionRegistration(dataValue(value, "session")) !== null;
142
+ }
143
+
144
+ /** Exact outer registered frame for the one mode requested by this client. */
145
+ export function isExactRegisteredFrame(
146
+ value: unknown,
147
+ mode: "ordinary" | "remote" | "boss",
148
+ ): value is Record<string, unknown> {
149
+ try {
150
+ const modeKeys = mode === "remote"
151
+ ? ["remoteAccess", "access"]
152
+ : mode === "boss"
153
+ ? ["capabilities", "boss"]
154
+ : [];
155
+ if (!isExactPlainDataRecord(
156
+ value,
157
+ ["type", "sessionId", "protocol", "version", ...modeKeys],
158
+ [],
159
+ )) return false;
160
+ return dataValue(value, "type") === "registered";
161
+ } catch {
162
+ return false;
163
+ }
164
+ }
165
+
166
+ /** Reads a frame discriminant without invoking accessors or inherited state. */
167
+ export function readExactFrameType(value: unknown): string | null {
168
+ if (typeof value !== "object" || value === null || nodeUtilTypes.isProxy(value)) return null;
169
+ const descriptor = Object.getOwnPropertyDescriptor(value, "type");
170
+ return descriptor?.enumerable && Object.hasOwn(descriptor, "value") && typeof descriptor.value === "string"
171
+ ? descriptor.value
172
+ : null;
173
+ }