@ai-matrx/desktop-protocol 0.4.0 → 0.6.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/dist/index.d.cts CHANGED
@@ -1,13 +1,20 @@
1
1
  import { z } from 'zod';
2
- export { B as BEARER_PREFIX, e as CancelMsg, f as ClientToCoreMsg, C as ClientType, g as CloseCode, h as CloseCodeValue, j as CoreToClientMsg, k as CreditMsg, D as DesktopProtocolError, d as EVENTS, l as EndMsg, E as EndReason, m as ErrorCode, n as ErrorCodeName, o as ErrorData, p as ErrorMsg, q as ErrorReason, r as ErrorReasonName, s as EventMsg, t as EventName, F as FsPatchEditFailure, H as HelloMsg, L as Limits, N as NAMED_SCHEMAS, u as NamedSchemaId, c as OPS, v as OpDescriptor, O as OpName, a as OpParams, b as OpResult, w as OpSpec, P as PING_TEXT, x as PONG_TEXT, y as ProtocolErrorBody, R as RelayMsg, z as RequestMsg, A as ResponseMsg, G as SUBPROTOCOL, S as StreamMeta, I as StreamMsg, W as Welcome, i as isDesktopProtocolError } from './registry-CI-NpQ6L.cjs';
2
+ import { O as OpName } from './registry-jTmq1CsI.cjs';
3
+ export { B as BEARER_PREFIX, e as CancelMsg, f as ClientToCoreMsg, C as ClientType, g as CloseCode, h as CloseCodeValue, j as CoreToClientMsg, k as CreditMsg, D as DesktopProtocolError, d as EVENTS, l as EndMsg, E as EndReason, m as ErrorCode, n as ErrorCodeName, o as ErrorData, p as ErrorMsg, q as ErrorReason, r as ErrorReasonName, s as EventMsg, t as EventName, F as FsPatchEditFailure, H as HelloMsg, L as Limits, N as NAMED_SCHEMAS, u as NamedSchemaId, c as OPS, v as OpDescriptor, a as OpParams, b as OpResult, w as OpSpec, P as PING_TEXT, x as PONG_TEXT, y as ProtocolErrorBody, R as RelayMsg, z as RequestMsg, A as ResponseMsg, G as SUBPROTOCOL, S as StreamMeta, I as StreamMsg, W as Welcome, i as isDesktopProtocolError } from './registry-jTmq1CsI.cjs';
3
4
  export { BROADCAST_CID, DecodedFrame, FRAME_HEADER_BYTES, FRAME_STRUCT_FORMAT, FRAME_VERSION, FrameChannel, FrameChannelValue, FrameFlag, assertFrameHeader, connectionEndFrame, decodeFrame, encodeFrame, hasFlag, isConnectionEnd, readCid, stampCid } from './frame.cjs';
4
5
 
5
6
  /** @ai-matrx/desktop-protocol — version and primitive schemas. Schema source: lint-enforced rules in scripts/check-schema-source.mjs. */
6
7
 
7
8
  declare const PROTOCOL_MAJOR: 1;
8
- declare const PROTOCOL_MINOR: 0;
9
+ /**
10
+ * Minor history (same major = compatible; see src/versioning.ts for the rule and FIELDS_SINCE):
11
+ * 1.0 — the first contract.
12
+ * 1.1 — fs.patch per-edit results; /call reports the negotiated version;
13
+ * device status since_ms is null for a device that never connected.
14
+ */
15
+ declare const PROTOCOL_MINOR: 1;
9
16
  /** This package's version as the wire spells it ("major.minor"): hello, welcome, relay status. */
10
- declare const PROTOCOL_VERSION: "1.0";
17
+ declare const PROTOCOL_VERSION: "1.1";
11
18
  /** Internal building blocks — deliberately unnamed in $defs (inlined wherever used). */
12
19
  declare const u32: z.ZodInt;
13
20
  declare const nonNegInt: z.ZodInt;
@@ -377,7 +384,7 @@ declare const FsPatchResult: z.ZodObject<{
377
384
  applied: z.ZodInt;
378
385
  size: z.ZodInt;
379
386
  mtime_ms: z.ZodInt;
380
- edits_applied: z.ZodArray<z.ZodObject<{
387
+ edits_applied: z.ZodOptional<z.ZodArray<z.ZodObject<{
381
388
  edit_index: z.ZodInt;
382
389
  mode: z.ZodEnum<{
383
390
  replace_all: "replace_all";
@@ -385,11 +392,11 @@ declare const FsPatchResult: z.ZodObject<{
385
392
  replace: "replace";
386
393
  }>;
387
394
  matches_replaced: z.ZodOptional<z.ZodInt>;
388
- }, z.core.$strict>>;
389
- edits_failed: z.ZodArray<z.ZodObject<{
395
+ }, z.core.$strict>>>;
396
+ edits_failed: z.ZodOptional<z.ZodArray<z.ZodObject<{
390
397
  edit_index: z.ZodInt;
391
398
  reason: z.ZodString;
392
- }, z.core.$strict>>;
399
+ }, z.core.$strict>>>;
393
400
  }, z.core.$strict>;
394
401
  type FsPatchResult = z.output<typeof FsPatchResult>;
395
402
  /** One edit fs.patch applied — the daemon's `edits_applied[]` entry, verbatim. */
@@ -901,6 +908,11 @@ type SessionControlChangedEvent = z.output<typeof SessionControlChangedEvent>;
901
908
 
902
909
  /** @ai-matrx/desktop-protocol — cap relay. Schema source: lint-enforced rules in scripts/check-schema-source.mjs. */
903
910
 
911
+ /**
912
+ * The caller of /call announces the newest protocol it reads in this request header; the relay
913
+ * includes fields newer than 1.0 only when the announced version knows them (src/versioning.ts).
914
+ */
915
+ declare const RELAY_CALL_VERSION_HEADER: "x-matrx-protocol-version";
904
916
  /**
905
917
  * How the relay runs one /call on the device: a fresh cid (its own connection) carrying exactly
906
918
  * RelayCallHello then RelayCallEnvelope, answered on that cid by response/error with id "call"
@@ -910,7 +922,7 @@ declare const RELAY_CALL_HELLO_ID: "hello";
910
922
  declare const RELAY_CALL_REQUEST_ID: "call";
911
923
  declare const RelayDeviceStatusEvent: z.ZodObject<{
912
924
  online: z.ZodBoolean;
913
- since_ms: z.ZodInt;
925
+ since_ms: z.ZodNullable<z.ZodInt>;
914
926
  app_version: z.ZodNullable<z.ZodString>;
915
927
  protocol_version: z.ZodNullable<z.ZodString>;
916
928
  }, z.core.$strict>;
@@ -930,6 +942,7 @@ type RelayCallRequest = z.output<typeof RelayCallRequest>;
930
942
  declare const RelayCallResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
931
943
  ok: z.ZodLiteral<true>;
932
944
  result: z.ZodUnknown;
945
+ protocol_version: z.ZodOptional<z.ZodString>;
933
946
  }, z.core.$strict>, z.ZodObject<{
934
947
  ok: z.ZodLiteral<false>;
935
948
  error: z.ZodObject<{
@@ -991,12 +1004,13 @@ declare const RelayCallResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
991
1004
  }, z.core.$strict>>;
992
1005
  }, z.core.$strict>>;
993
1006
  }, z.core.$strict>;
1007
+ protocol_version: z.ZodOptional<z.ZodString>;
994
1008
  }, z.core.$strict>], "ok">;
995
1009
  type RelayCallResponse = z.output<typeof RelayCallResponse>;
996
1010
  /** HTTP GET /v1/devices/{device_id}/status (owner JWT). */
997
1011
  declare const RelayStatusResponse: z.ZodObject<{
998
1012
  online: z.ZodBoolean;
999
- since_ms: z.ZodInt;
1013
+ since_ms: z.ZodNullable<z.ZodInt>;
1000
1014
  app_version: z.ZodNullable<z.ZodString>;
1001
1015
  protocol_version: z.ZodNullable<z.ZodString>;
1002
1016
  }, z.core.$strict>;
@@ -1018,4 +1032,75 @@ declare const RelayCallEnvelope: z.ZodObject<{
1018
1032
  }, z.core.$strict>;
1019
1033
  type RelayCallEnvelope = z.output<typeof RelayCallEnvelope>;
1020
1034
 
1021
- export { AbsPath, DEFAULT_MAX_FRAME_PAYLOAD_BYTES, DEFAULT_MAX_MESSAGE_BYTES, DEFAULT_WINDOW_BYTES, DeviceId, ExecCwdEvent, ExecExitedEvent, ExecListParams, ExecListResult, ExecPtyKillParams, ExecPtyKillResult, ExecPtyResizeParams, ExecPtyResizeResult, ExecPtySignalParams, ExecPtySignalResult, ExecPtyStartParams, ExecPtyStartResult, ExecPtyWriteParams, ExecPtyWriteResult, ExecRunParams, ExecRunResult, ExecTitleEvent, FrameHeader, FsChange, FsChangedEvent, FsDeleteParams, FsDeleteResult, FsEncoding, FsEntry, FsKind, FsListParams, FsListResult, FsMkdirParams, FsMkdirResult, FsMoveParams, FsMoveResult, FsPatchEditApplied, FsPatchParams, FsPatchResult, FsReadParams, FsReadResult, FsReadStreamParams, FsReadStreamResult, FsSearchContentParams, FsSearchContentResult, FsSearchPathsParams, FsSearchPathsResult, FsStatParams, FsStatResult, FsWatchParams, FsWatchResult, FsWriteParams, FsWriteResult, OsPermissionName, PROTOCOL_MAJOR, PROTOCOL_MINOR, PROTOCOL_VERSION, ProtocolVersion, PtyInfo, RELAY_CALL_HELLO_ID, RELAY_CALL_REQUEST_ID, RelayAuthExpiringEvent, RelayCallEnvelope, RelayCallHello, RelayCallRequest, RelayCallResponse, RelayDeviceStatusEvent, RelayHopConnectionEnd, RelayHopEnvelopeHeader, RelayStatusResponse, RequestId, ResourceId, ResourceInfo, SessionAttachParams, SessionAttachResult, SessionControlChangedEvent, SessionDetachParams, SessionDetachResult, SessionListParams, SessionListResult, SessionReleaseParams, SessionReleaseResult, Signal, SysinfoGetParams, SysinfoGetResult, epochMs, nonNegInt, u32 };
1035
+ /**
1036
+ * Version skew is the normal state: a phone, the server and a desktop app in the field run
1037
+ * different package versions, and a minor protocol bump must never make any of them fail.
1038
+ *
1039
+ * THE RULE (enforced by src/versioning.test.ts against generated/baseline-1.0.json):
1040
+ * 1. Within a major, a minor may only ADD. A field added after x.0 is OPTIONAL in the schema
1041
+ * (an older peer omits it) and is listed in FIELDS_SINCE with the minor that added it.
1042
+ * 2. A writer never sends a field newer than the version its peer negotiated (hello/welcome, or
1043
+ * the /call version header): older peers' strict schemas refuse unknown keys. `forPeer`
1044
+ * does the stripping; the core's Router applies it to every result and error it sends.
1045
+ * 3. A reader that needs a newer field checks the peer's version and says so loudly when the
1046
+ * peer is older (e.g. aidream's local_proxy marks a patch answer from a 1.0 app).
1047
+ */
1048
+
1049
+ /**
1050
+ * Fields added after x.0, by schema location: a named schema (`FsPatchResult`), a property path
1051
+ * inside one (`ProtocolErrorBody.data`), or a union branch (`RelayCallResponse|0`).
1052
+ */
1053
+ declare const FIELDS_SINCE: {
1054
+ readonly FsPatchResult: {
1055
+ readonly edits_applied: "1.1";
1056
+ readonly edits_failed: "1.1";
1057
+ };
1058
+ readonly "ProtocolErrorBody.data": {
1059
+ readonly edits_failed: "1.1";
1060
+ };
1061
+ readonly "RelayCallResponse|0": {
1062
+ readonly protocol_version: "1.1";
1063
+ };
1064
+ readonly "RelayCallResponse|1": {
1065
+ readonly protocol_version: "1.1";
1066
+ };
1067
+ };
1068
+ type FieldsSincePath = keyof typeof FIELDS_SINCE;
1069
+ /**
1070
+ * 1.0 fields that a later minor lets be null, and what a peer older than that minor is sent
1071
+ * instead (the value that meant the same thing before). Widening a field any other way is a major.
1072
+ */
1073
+ declare const NULL_SINCE: {
1074
+ readonly "RelayDeviceStatusEvent.since_ms": {
1075
+ readonly since: "1.1";
1076
+ readonly olderPeersGet: 0;
1077
+ };
1078
+ };
1079
+ /** A device status for a reader at `version`: `since_ms` null ("never") becomes 0 before 1.1. */
1080
+ declare function statusForPeer<T extends {
1081
+ since_ms: number | null;
1082
+ }>(status: T, version: string): T;
1083
+ /**
1084
+ * A WebSocket client announces the newest minor it reads at connect time by also offering
1085
+ * `matrx.v<major>.<minor>` beside SUBPROTOCOL (servers still answer SUBPROTOCOL). Before hello is
1086
+ * possible, this is how the relay picks the right shape for its greeting.
1087
+ */
1088
+ declare function versionSubprotocol(version: string): string;
1089
+ /** The version a client announced in its offered subprotocols; "1.0" when it did not. */
1090
+ declare function announcedVersion(offered: readonly string[]): string;
1091
+ /** "1.10" > "1.9". Same-major comparison of "major.minor" strings. */
1092
+ declare function compareVersions(a: string, b: string): number;
1093
+ /** True when a peer at `version` knows a field added in `since`. */
1094
+ declare function knows(version: string, since: string): boolean;
1095
+ /** The version two peers speak: the lower minor within this major. */
1096
+ declare function negotiate(peerVersion: string): string;
1097
+ /**
1098
+ * What to send a peer that negotiated `version`: `result` of `op`, or an error body, with every
1099
+ * field newer than that version removed (rule 2). Unchanged for a current peer.
1100
+ */
1101
+ declare function forPeer(op: OpName, part: "result", value: unknown, version: string): unknown;
1102
+ declare function forPeer(op: OpName | null, part: "error", value: unknown, version: string): unknown;
1103
+ /** Every op whose result has a versioned field is wired into forPeer (checked by the test). */
1104
+ declare const VERSIONED_RESULT_OPS: readonly OpName[];
1105
+
1106
+ export { AbsPath, DEFAULT_MAX_FRAME_PAYLOAD_BYTES, DEFAULT_MAX_MESSAGE_BYTES, DEFAULT_WINDOW_BYTES, DeviceId, ExecCwdEvent, ExecExitedEvent, ExecListParams, ExecListResult, ExecPtyKillParams, ExecPtyKillResult, ExecPtyResizeParams, ExecPtyResizeResult, ExecPtySignalParams, ExecPtySignalResult, ExecPtyStartParams, ExecPtyStartResult, ExecPtyWriteParams, ExecPtyWriteResult, ExecRunParams, ExecRunResult, ExecTitleEvent, FIELDS_SINCE, type FieldsSincePath, FrameHeader, FsChange, FsChangedEvent, FsDeleteParams, FsDeleteResult, FsEncoding, FsEntry, FsKind, FsListParams, FsListResult, FsMkdirParams, FsMkdirResult, FsMoveParams, FsMoveResult, FsPatchEditApplied, FsPatchParams, FsPatchResult, FsReadParams, FsReadResult, FsReadStreamParams, FsReadStreamResult, FsSearchContentParams, FsSearchContentResult, FsSearchPathsParams, FsSearchPathsResult, FsStatParams, FsStatResult, FsWatchParams, FsWatchResult, FsWriteParams, FsWriteResult, NULL_SINCE, OpName, OsPermissionName, PROTOCOL_MAJOR, PROTOCOL_MINOR, PROTOCOL_VERSION, ProtocolVersion, PtyInfo, RELAY_CALL_HELLO_ID, RELAY_CALL_REQUEST_ID, RELAY_CALL_VERSION_HEADER, RelayAuthExpiringEvent, RelayCallEnvelope, RelayCallHello, RelayCallRequest, RelayCallResponse, RelayDeviceStatusEvent, RelayHopConnectionEnd, RelayHopEnvelopeHeader, RelayStatusResponse, RequestId, ResourceId, ResourceInfo, SessionAttachParams, SessionAttachResult, SessionControlChangedEvent, SessionDetachParams, SessionDetachResult, SessionListParams, SessionListResult, SessionReleaseParams, SessionReleaseResult, Signal, SysinfoGetParams, SysinfoGetResult, VERSIONED_RESULT_OPS, announcedVersion, compareVersions, epochMs, forPeer, knows, negotiate, nonNegInt, statusForPeer, u32, versionSubprotocol };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  import { z } from 'zod';
2
- export { B as BEARER_PREFIX, e as CancelMsg, f as ClientToCoreMsg, C as ClientType, g as CloseCode, h as CloseCodeValue, j as CoreToClientMsg, k as CreditMsg, D as DesktopProtocolError, d as EVENTS, l as EndMsg, E as EndReason, m as ErrorCode, n as ErrorCodeName, o as ErrorData, p as ErrorMsg, q as ErrorReason, r as ErrorReasonName, s as EventMsg, t as EventName, F as FsPatchEditFailure, H as HelloMsg, L as Limits, N as NAMED_SCHEMAS, u as NamedSchemaId, c as OPS, v as OpDescriptor, O as OpName, a as OpParams, b as OpResult, w as OpSpec, P as PING_TEXT, x as PONG_TEXT, y as ProtocolErrorBody, R as RelayMsg, z as RequestMsg, A as ResponseMsg, G as SUBPROTOCOL, S as StreamMeta, I as StreamMsg, W as Welcome, i as isDesktopProtocolError } from './registry-CI-NpQ6L.js';
2
+ import { O as OpName } from './registry-jTmq1CsI.js';
3
+ export { B as BEARER_PREFIX, e as CancelMsg, f as ClientToCoreMsg, C as ClientType, g as CloseCode, h as CloseCodeValue, j as CoreToClientMsg, k as CreditMsg, D as DesktopProtocolError, d as EVENTS, l as EndMsg, E as EndReason, m as ErrorCode, n as ErrorCodeName, o as ErrorData, p as ErrorMsg, q as ErrorReason, r as ErrorReasonName, s as EventMsg, t as EventName, F as FsPatchEditFailure, H as HelloMsg, L as Limits, N as NAMED_SCHEMAS, u as NamedSchemaId, c as OPS, v as OpDescriptor, a as OpParams, b as OpResult, w as OpSpec, P as PING_TEXT, x as PONG_TEXT, y as ProtocolErrorBody, R as RelayMsg, z as RequestMsg, A as ResponseMsg, G as SUBPROTOCOL, S as StreamMeta, I as StreamMsg, W as Welcome, i as isDesktopProtocolError } from './registry-jTmq1CsI.js';
3
4
  export { BROADCAST_CID, DecodedFrame, FRAME_HEADER_BYTES, FRAME_STRUCT_FORMAT, FRAME_VERSION, FrameChannel, FrameChannelValue, FrameFlag, assertFrameHeader, connectionEndFrame, decodeFrame, encodeFrame, hasFlag, isConnectionEnd, readCid, stampCid } from './frame.js';
4
5
 
5
6
  /** @ai-matrx/desktop-protocol — version and primitive schemas. Schema source: lint-enforced rules in scripts/check-schema-source.mjs. */
6
7
 
7
8
  declare const PROTOCOL_MAJOR: 1;
8
- declare const PROTOCOL_MINOR: 0;
9
+ /**
10
+ * Minor history (same major = compatible; see src/versioning.ts for the rule and FIELDS_SINCE):
11
+ * 1.0 — the first contract.
12
+ * 1.1 — fs.patch per-edit results; /call reports the negotiated version;
13
+ * device status since_ms is null for a device that never connected.
14
+ */
15
+ declare const PROTOCOL_MINOR: 1;
9
16
  /** This package's version as the wire spells it ("major.minor"): hello, welcome, relay status. */
10
- declare const PROTOCOL_VERSION: "1.0";
17
+ declare const PROTOCOL_VERSION: "1.1";
11
18
  /** Internal building blocks — deliberately unnamed in $defs (inlined wherever used). */
12
19
  declare const u32: z.ZodInt;
13
20
  declare const nonNegInt: z.ZodInt;
@@ -377,7 +384,7 @@ declare const FsPatchResult: z.ZodObject<{
377
384
  applied: z.ZodInt;
378
385
  size: z.ZodInt;
379
386
  mtime_ms: z.ZodInt;
380
- edits_applied: z.ZodArray<z.ZodObject<{
387
+ edits_applied: z.ZodOptional<z.ZodArray<z.ZodObject<{
381
388
  edit_index: z.ZodInt;
382
389
  mode: z.ZodEnum<{
383
390
  replace_all: "replace_all";
@@ -385,11 +392,11 @@ declare const FsPatchResult: z.ZodObject<{
385
392
  replace: "replace";
386
393
  }>;
387
394
  matches_replaced: z.ZodOptional<z.ZodInt>;
388
- }, z.core.$strict>>;
389
- edits_failed: z.ZodArray<z.ZodObject<{
395
+ }, z.core.$strict>>>;
396
+ edits_failed: z.ZodOptional<z.ZodArray<z.ZodObject<{
390
397
  edit_index: z.ZodInt;
391
398
  reason: z.ZodString;
392
- }, z.core.$strict>>;
399
+ }, z.core.$strict>>>;
393
400
  }, z.core.$strict>;
394
401
  type FsPatchResult = z.output<typeof FsPatchResult>;
395
402
  /** One edit fs.patch applied — the daemon's `edits_applied[]` entry, verbatim. */
@@ -901,6 +908,11 @@ type SessionControlChangedEvent = z.output<typeof SessionControlChangedEvent>;
901
908
 
902
909
  /** @ai-matrx/desktop-protocol — cap relay. Schema source: lint-enforced rules in scripts/check-schema-source.mjs. */
903
910
 
911
+ /**
912
+ * The caller of /call announces the newest protocol it reads in this request header; the relay
913
+ * includes fields newer than 1.0 only when the announced version knows them (src/versioning.ts).
914
+ */
915
+ declare const RELAY_CALL_VERSION_HEADER: "x-matrx-protocol-version";
904
916
  /**
905
917
  * How the relay runs one /call on the device: a fresh cid (its own connection) carrying exactly
906
918
  * RelayCallHello then RelayCallEnvelope, answered on that cid by response/error with id "call"
@@ -910,7 +922,7 @@ declare const RELAY_CALL_HELLO_ID: "hello";
910
922
  declare const RELAY_CALL_REQUEST_ID: "call";
911
923
  declare const RelayDeviceStatusEvent: z.ZodObject<{
912
924
  online: z.ZodBoolean;
913
- since_ms: z.ZodInt;
925
+ since_ms: z.ZodNullable<z.ZodInt>;
914
926
  app_version: z.ZodNullable<z.ZodString>;
915
927
  protocol_version: z.ZodNullable<z.ZodString>;
916
928
  }, z.core.$strict>;
@@ -930,6 +942,7 @@ type RelayCallRequest = z.output<typeof RelayCallRequest>;
930
942
  declare const RelayCallResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
931
943
  ok: z.ZodLiteral<true>;
932
944
  result: z.ZodUnknown;
945
+ protocol_version: z.ZodOptional<z.ZodString>;
933
946
  }, z.core.$strict>, z.ZodObject<{
934
947
  ok: z.ZodLiteral<false>;
935
948
  error: z.ZodObject<{
@@ -991,12 +1004,13 @@ declare const RelayCallResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
991
1004
  }, z.core.$strict>>;
992
1005
  }, z.core.$strict>>;
993
1006
  }, z.core.$strict>;
1007
+ protocol_version: z.ZodOptional<z.ZodString>;
994
1008
  }, z.core.$strict>], "ok">;
995
1009
  type RelayCallResponse = z.output<typeof RelayCallResponse>;
996
1010
  /** HTTP GET /v1/devices/{device_id}/status (owner JWT). */
997
1011
  declare const RelayStatusResponse: z.ZodObject<{
998
1012
  online: z.ZodBoolean;
999
- since_ms: z.ZodInt;
1013
+ since_ms: z.ZodNullable<z.ZodInt>;
1000
1014
  app_version: z.ZodNullable<z.ZodString>;
1001
1015
  protocol_version: z.ZodNullable<z.ZodString>;
1002
1016
  }, z.core.$strict>;
@@ -1018,4 +1032,75 @@ declare const RelayCallEnvelope: z.ZodObject<{
1018
1032
  }, z.core.$strict>;
1019
1033
  type RelayCallEnvelope = z.output<typeof RelayCallEnvelope>;
1020
1034
 
1021
- export { AbsPath, DEFAULT_MAX_FRAME_PAYLOAD_BYTES, DEFAULT_MAX_MESSAGE_BYTES, DEFAULT_WINDOW_BYTES, DeviceId, ExecCwdEvent, ExecExitedEvent, ExecListParams, ExecListResult, ExecPtyKillParams, ExecPtyKillResult, ExecPtyResizeParams, ExecPtyResizeResult, ExecPtySignalParams, ExecPtySignalResult, ExecPtyStartParams, ExecPtyStartResult, ExecPtyWriteParams, ExecPtyWriteResult, ExecRunParams, ExecRunResult, ExecTitleEvent, FrameHeader, FsChange, FsChangedEvent, FsDeleteParams, FsDeleteResult, FsEncoding, FsEntry, FsKind, FsListParams, FsListResult, FsMkdirParams, FsMkdirResult, FsMoveParams, FsMoveResult, FsPatchEditApplied, FsPatchParams, FsPatchResult, FsReadParams, FsReadResult, FsReadStreamParams, FsReadStreamResult, FsSearchContentParams, FsSearchContentResult, FsSearchPathsParams, FsSearchPathsResult, FsStatParams, FsStatResult, FsWatchParams, FsWatchResult, FsWriteParams, FsWriteResult, OsPermissionName, PROTOCOL_MAJOR, PROTOCOL_MINOR, PROTOCOL_VERSION, ProtocolVersion, PtyInfo, RELAY_CALL_HELLO_ID, RELAY_CALL_REQUEST_ID, RelayAuthExpiringEvent, RelayCallEnvelope, RelayCallHello, RelayCallRequest, RelayCallResponse, RelayDeviceStatusEvent, RelayHopConnectionEnd, RelayHopEnvelopeHeader, RelayStatusResponse, RequestId, ResourceId, ResourceInfo, SessionAttachParams, SessionAttachResult, SessionControlChangedEvent, SessionDetachParams, SessionDetachResult, SessionListParams, SessionListResult, SessionReleaseParams, SessionReleaseResult, Signal, SysinfoGetParams, SysinfoGetResult, epochMs, nonNegInt, u32 };
1035
+ /**
1036
+ * Version skew is the normal state: a phone, the server and a desktop app in the field run
1037
+ * different package versions, and a minor protocol bump must never make any of them fail.
1038
+ *
1039
+ * THE RULE (enforced by src/versioning.test.ts against generated/baseline-1.0.json):
1040
+ * 1. Within a major, a minor may only ADD. A field added after x.0 is OPTIONAL in the schema
1041
+ * (an older peer omits it) and is listed in FIELDS_SINCE with the minor that added it.
1042
+ * 2. A writer never sends a field newer than the version its peer negotiated (hello/welcome, or
1043
+ * the /call version header): older peers' strict schemas refuse unknown keys. `forPeer`
1044
+ * does the stripping; the core's Router applies it to every result and error it sends.
1045
+ * 3. A reader that needs a newer field checks the peer's version and says so loudly when the
1046
+ * peer is older (e.g. aidream's local_proxy marks a patch answer from a 1.0 app).
1047
+ */
1048
+
1049
+ /**
1050
+ * Fields added after x.0, by schema location: a named schema (`FsPatchResult`), a property path
1051
+ * inside one (`ProtocolErrorBody.data`), or a union branch (`RelayCallResponse|0`).
1052
+ */
1053
+ declare const FIELDS_SINCE: {
1054
+ readonly FsPatchResult: {
1055
+ readonly edits_applied: "1.1";
1056
+ readonly edits_failed: "1.1";
1057
+ };
1058
+ readonly "ProtocolErrorBody.data": {
1059
+ readonly edits_failed: "1.1";
1060
+ };
1061
+ readonly "RelayCallResponse|0": {
1062
+ readonly protocol_version: "1.1";
1063
+ };
1064
+ readonly "RelayCallResponse|1": {
1065
+ readonly protocol_version: "1.1";
1066
+ };
1067
+ };
1068
+ type FieldsSincePath = keyof typeof FIELDS_SINCE;
1069
+ /**
1070
+ * 1.0 fields that a later minor lets be null, and what a peer older than that minor is sent
1071
+ * instead (the value that meant the same thing before). Widening a field any other way is a major.
1072
+ */
1073
+ declare const NULL_SINCE: {
1074
+ readonly "RelayDeviceStatusEvent.since_ms": {
1075
+ readonly since: "1.1";
1076
+ readonly olderPeersGet: 0;
1077
+ };
1078
+ };
1079
+ /** A device status for a reader at `version`: `since_ms` null ("never") becomes 0 before 1.1. */
1080
+ declare function statusForPeer<T extends {
1081
+ since_ms: number | null;
1082
+ }>(status: T, version: string): T;
1083
+ /**
1084
+ * A WebSocket client announces the newest minor it reads at connect time by also offering
1085
+ * `matrx.v<major>.<minor>` beside SUBPROTOCOL (servers still answer SUBPROTOCOL). Before hello is
1086
+ * possible, this is how the relay picks the right shape for its greeting.
1087
+ */
1088
+ declare function versionSubprotocol(version: string): string;
1089
+ /** The version a client announced in its offered subprotocols; "1.0" when it did not. */
1090
+ declare function announcedVersion(offered: readonly string[]): string;
1091
+ /** "1.10" > "1.9". Same-major comparison of "major.minor" strings. */
1092
+ declare function compareVersions(a: string, b: string): number;
1093
+ /** True when a peer at `version` knows a field added in `since`. */
1094
+ declare function knows(version: string, since: string): boolean;
1095
+ /** The version two peers speak: the lower minor within this major. */
1096
+ declare function negotiate(peerVersion: string): string;
1097
+ /**
1098
+ * What to send a peer that negotiated `version`: `result` of `op`, or an error body, with every
1099
+ * field newer than that version removed (rule 2). Unchanged for a current peer.
1100
+ */
1101
+ declare function forPeer(op: OpName, part: "result", value: unknown, version: string): unknown;
1102
+ declare function forPeer(op: OpName | null, part: "error", value: unknown, version: string): unknown;
1103
+ /** Every op whose result has a versioned field is wired into forPeer (checked by the test). */
1104
+ declare const VERSIONED_RESULT_OPS: readonly OpName[];
1105
+
1106
+ export { AbsPath, DEFAULT_MAX_FRAME_PAYLOAD_BYTES, DEFAULT_MAX_MESSAGE_BYTES, DEFAULT_WINDOW_BYTES, DeviceId, ExecCwdEvent, ExecExitedEvent, ExecListParams, ExecListResult, ExecPtyKillParams, ExecPtyKillResult, ExecPtyResizeParams, ExecPtyResizeResult, ExecPtySignalParams, ExecPtySignalResult, ExecPtyStartParams, ExecPtyStartResult, ExecPtyWriteParams, ExecPtyWriteResult, ExecRunParams, ExecRunResult, ExecTitleEvent, FIELDS_SINCE, type FieldsSincePath, FrameHeader, FsChange, FsChangedEvent, FsDeleteParams, FsDeleteResult, FsEncoding, FsEntry, FsKind, FsListParams, FsListResult, FsMkdirParams, FsMkdirResult, FsMoveParams, FsMoveResult, FsPatchEditApplied, FsPatchParams, FsPatchResult, FsReadParams, FsReadResult, FsReadStreamParams, FsReadStreamResult, FsSearchContentParams, FsSearchContentResult, FsSearchPathsParams, FsSearchPathsResult, FsStatParams, FsStatResult, FsWatchParams, FsWatchResult, FsWriteParams, FsWriteResult, NULL_SINCE, OpName, OsPermissionName, PROTOCOL_MAJOR, PROTOCOL_MINOR, PROTOCOL_VERSION, ProtocolVersion, PtyInfo, RELAY_CALL_HELLO_ID, RELAY_CALL_REQUEST_ID, RELAY_CALL_VERSION_HEADER, RelayAuthExpiringEvent, RelayCallEnvelope, RelayCallHello, RelayCallRequest, RelayCallResponse, RelayDeviceStatusEvent, RelayHopConnectionEnd, RelayHopEnvelopeHeader, RelayStatusResponse, RequestId, ResourceId, ResourceInfo, SessionAttachParams, SessionAttachResult, SessionControlChangedEvent, SessionDetachParams, SessionDetachResult, SessionListParams, SessionListResult, SessionReleaseParams, SessionReleaseResult, Signal, SysinfoGetParams, SysinfoGetResult, VERSIONED_RESULT_OPS, announcedVersion, compareVersions, epochMs, forPeer, knows, negotiate, nonNegInt, statusForPeer, u32, versionSubprotocol };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/schema/primitives.ts
2
2
  import { z } from "zod";
3
3
  var PROTOCOL_MAJOR = 1;
4
- var PROTOCOL_MINOR = 0;
4
+ var PROTOCOL_MINOR = 1;
5
5
  var PROTOCOL_VERSION = `${PROTOCOL_MAJOR}.${PROTOCOL_MINOR}`;
6
6
  var ProtocolVersion = z.string().regex(/^[1-9][0-9]{0,2}\.[0-9]{1,3}$/);
7
7
  var RequestId = z.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
@@ -495,8 +495,10 @@ var FsPatchResult = z6.strictObject({
495
495
  applied: nonNegInt,
496
496
  size: nonNegInt,
497
497
  mtime_ms: epochMs,
498
- edits_applied: z6.array(FsPatchEditApplied).max(256),
499
- edits_failed: z6.array(FsPatchEditFailure).max(256)
498
+ /** Since 1.1 (FIELDS_SINCE): a 1.0 app sends only `applied`. Always sent to a ≥1.1 peer. */
499
+ edits_applied: z6.array(FsPatchEditApplied).max(256).optional(),
500
+ /** Since 1.1 (FIELDS_SINCE). */
501
+ edits_failed: z6.array(FsPatchEditFailure).max(256).optional()
500
502
  });
501
503
  var FsSearchPathsParams = z6.strictObject({
502
504
  pattern: z6.string().min(1).max(512),
@@ -636,7 +638,11 @@ var SessionControlChangedEvent = z8.strictObject({ controller_client_id: z8.stri
636
638
  import { z as z9 } from "zod";
637
639
  var RelayDeviceStatusEvent = z9.strictObject({
638
640
  online: z9.boolean(),
639
- since_ms: epochMs,
641
+ /**
642
+ * When the device last went online/offline; null = it has never connected (since 1.1 —
643
+ * NULL_SINCE: a 1.0 reader is sent 0, its old meaning).
644
+ */
645
+ since_ms: epochMs.nullable(),
640
646
  app_version: z9.string().max(32).nullable(),
641
647
  protocol_version: ProtocolVersion.nullable()
642
648
  });
@@ -647,9 +653,20 @@ var RelayCallRequest = z9.strictObject({
647
653
  params: z9.unknown(),
648
654
  timeout_ms: z9.int().min(100).max(37e5).default(7e4)
649
655
  });
656
+ var RELAY_CALL_VERSION_HEADER = "x-matrx-protocol-version";
650
657
  var RelayCallResponse = z9.discriminatedUnion("ok", [
651
- z9.strictObject({ ok: z9.literal(true), result: z9.unknown() }),
652
- z9.strictObject({ ok: z9.literal(false), error: ProtocolErrorBody })
658
+ z9.strictObject({
659
+ ok: z9.literal(true),
660
+ result: z9.unknown(),
661
+ /** Since 1.1: the version the device negotiated for this call (its welcome). */
662
+ protocol_version: ProtocolVersion.optional()
663
+ }),
664
+ z9.strictObject({
665
+ ok: z9.literal(false),
666
+ error: ProtocolErrorBody,
667
+ /** Since 1.1, as above (absent when the device never answered hello). */
668
+ protocol_version: ProtocolVersion.optional()
669
+ })
653
670
  ]);
654
671
  var RELAY_CALL_HELLO_ID = "hello";
655
672
  var RELAY_CALL_REQUEST_ID = "call";
@@ -804,6 +821,69 @@ var NAMED_SCHEMAS = {
804
821
  RelayHopConnectionEnd,
805
822
  CloseCodeValue
806
823
  };
824
+
825
+ // src/versioning.ts
826
+ var FIELDS_SINCE = {
827
+ FsPatchResult: { edits_applied: "1.1", edits_failed: "1.1" },
828
+ "ProtocolErrorBody.data": { edits_failed: "1.1" },
829
+ "RelayCallResponse|0": { protocol_version: "1.1" },
830
+ "RelayCallResponse|1": { protocol_version: "1.1" }
831
+ };
832
+ var NULL_SINCE = {
833
+ "RelayDeviceStatusEvent.since_ms": { since: "1.1", olderPeersGet: 0 }
834
+ };
835
+ function statusForPeer(status, version) {
836
+ const rule = NULL_SINCE["RelayDeviceStatusEvent.since_ms"];
837
+ return status.since_ms === null && !knows(version, rule.since) ? { ...status, since_ms: rule.olderPeersGet } : status;
838
+ }
839
+ function versionSubprotocol(version) {
840
+ return `matrx.v${version}`;
841
+ }
842
+ function announcedVersion(offered) {
843
+ let best = `${PROTOCOL_MAJOR}.0`;
844
+ for (const p of offered) {
845
+ const m = /^matrx\.v(\d+\.\d+)$/.exec(p.trim());
846
+ if (m?.[1] && compareVersions(m[1], best) > 0 && m[1].startsWith(`${PROTOCOL_MAJOR}.`)) best = m[1];
847
+ }
848
+ return best;
849
+ }
850
+ function compareVersions(a, b) {
851
+ const [am = 0, an = 0] = a.split(".").map(Number);
852
+ const [bm = 0, bn = 0] = b.split(".").map(Number);
853
+ return am - bm || an - bn;
854
+ }
855
+ function knows(version, since) {
856
+ return compareVersions(version, since) >= 0;
857
+ }
858
+ function negotiate(peerVersion) {
859
+ const [major = 0, minor = 0] = peerVersion.split(".").map(Number);
860
+ if (major !== PROTOCOL_MAJOR) return `${PROTOCOL_MAJOR}.0`;
861
+ return `${PROTOCOL_MAJOR}.${Math.min(minor, PROTOCOL_MINOR)}`;
862
+ }
863
+ function strip(value, fields, version) {
864
+ if (!fields || value === null || typeof value !== "object" || Array.isArray(value)) return value;
865
+ let out = null;
866
+ for (const [field, since] of Object.entries(fields)) {
867
+ if (field in value && !knows(version, since)) {
868
+ out ??= { ...value };
869
+ delete out[field];
870
+ }
871
+ }
872
+ return out ?? value;
873
+ }
874
+ var RESULT_PATH = /* @__PURE__ */ new Map([["fs.patch", "FsPatchResult"]]);
875
+ function forPeer(op, part, value, version) {
876
+ if (part === "result") {
877
+ const path = op ? RESULT_PATH.get(op) : void 0;
878
+ return path ? strip(value, FIELDS_SINCE[path], version) : value;
879
+ }
880
+ if (value === null || typeof value !== "object") return value;
881
+ const body = value;
882
+ if (body.data === void 0) return value;
883
+ const data = strip(body.data, FIELDS_SINCE["ProtocolErrorBody.data"], version);
884
+ return data === body.data ? value : { ...body, data };
885
+ }
886
+ var VERSIONED_RESULT_OPS = [...RESULT_PATH.keys()];
807
887
  export {
808
888
  AbsPath,
809
889
  BEARER_PREFIX,
@@ -844,6 +924,7 @@ export {
844
924
  ExecRunParams,
845
925
  ExecRunResult,
846
926
  ExecTitleEvent,
927
+ FIELDS_SINCE,
847
928
  FRAME_HEADER_BYTES,
848
929
  FRAME_STRUCT_FORMAT,
849
930
  FRAME_VERSION,
@@ -884,6 +965,7 @@ export {
884
965
  HelloMsg,
885
966
  Limits,
886
967
  NAMED_SCHEMAS,
968
+ NULL_SINCE,
887
969
  OPS,
888
970
  OpDescriptor,
889
971
  OsPermissionName,
@@ -897,6 +979,7 @@ export {
897
979
  PtyInfo,
898
980
  RELAY_CALL_HELLO_ID,
899
981
  RELAY_CALL_REQUEST_ID,
982
+ RELAY_CALL_VERSION_HEADER,
900
983
  RelayAuthExpiringEvent,
901
984
  RelayCallEnvelope,
902
985
  RelayCallHello,
@@ -927,18 +1010,26 @@ export {
927
1010
  StreamMsg,
928
1011
  SysinfoGetParams,
929
1012
  SysinfoGetResult,
1013
+ VERSIONED_RESULT_OPS,
930
1014
  Welcome,
1015
+ announcedVersion,
931
1016
  assertFrameHeader,
1017
+ compareVersions,
932
1018
  connectionEndFrame,
933
1019
  decodeFrame,
934
1020
  encodeFrame,
935
1021
  epochMs,
1022
+ forPeer,
936
1023
  hasFlag,
937
1024
  isConnectionEnd,
938
1025
  isDesktopProtocolError,
1026
+ knows,
1027
+ negotiate,
939
1028
  nonNegInt,
940
1029
  readCid,
941
1030
  stampCid,
942
- u32
1031
+ statusForPeer,
1032
+ u32,
1033
+ versionSubprotocol
943
1034
  };
944
1035
  //# sourceMappingURL=index.js.map