@kangentic/protocol 0.18.0 → 0.19.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.cjs CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  PUSH_CATEGORIES: () => PUSH_CATEGORIES,
40
40
  PUSH_ENVELOPE_MAX_AGE_MS: () => PUSH_ENVELOPE_MAX_AGE_MS,
41
41
  PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS: () => PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
42
+ RESPONSE_TOO_LARGE_ERROR_CODE: () => RESPONSE_TOO_LARGE_ERROR_CODE,
42
43
  SecretstreamState: () => SecretstreamState,
43
44
  SessionFrameKind: () => SessionFrameKind,
44
45
  UNSUPPORTED_VERB_ERROR_CODE: () => UNSUPPORTED_VERB_ERROR_CODE,
@@ -58,6 +59,7 @@ __export(index_exports, {
58
59
  deriveSessionSlotId: () => deriveSessionSlotId,
59
60
  deriveShortAuthenticationString: () => deriveShortAuthenticationString,
60
61
  encodeMessage: () => encodeMessage,
62
+ encodeMessageFailure: () => encodeMessageFailure,
61
63
  encodePairingQrPayload: () => encodePairingQrPayload,
62
64
  encodeProtocolVersion: () => encodeProtocolVersion,
63
65
  encodeRosterEntryForSigning: () => encodeRosterEntryForSigning,
@@ -5010,6 +5012,7 @@ var FRAME_FORMAT_DEFLATE = 1;
5010
5012
  var JSON_OPEN_BRACE = 123;
5011
5013
  var DEFLATE_HEADER_LENGTH = 5;
5012
5014
  var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
5015
+ var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
5013
5016
  var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
5014
5017
  var UnsupportedVerbError = class extends Error {
5015
5018
  requestId;
@@ -5046,6 +5049,15 @@ function encodeMessage(message) {
5046
5049
  }
5047
5050
  return frame;
5048
5051
  }
5052
+ function encodeMessageFailure(message) {
5053
+ try {
5054
+ if (new TextEncoder().encode(JSON.stringify(message)).length <= MAX_FRAME_LENGTH) return null;
5055
+ encodeMessage(message);
5056
+ return null;
5057
+ } catch (error) {
5058
+ return error instanceof Error ? error.message : String(error);
5059
+ }
5060
+ }
5049
5061
  function decodeMessage(bytes) {
5050
5062
  if (bytes.length > MAX_FRAME_LENGTH) {
5051
5063
  throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
@@ -6246,6 +6258,7 @@ function isBridgeEvent(value) {
6246
6258
  PUSH_CATEGORIES,
6247
6259
  PUSH_ENVELOPE_MAX_AGE_MS,
6248
6260
  PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
6261
+ RESPONSE_TOO_LARGE_ERROR_CODE,
6249
6262
  SecretstreamState,
6250
6263
  SessionFrameKind,
6251
6264
  UNSUPPORTED_VERB_ERROR_CODE,
@@ -6265,6 +6278,7 @@ function isBridgeEvent(value) {
6265
6278
  deriveSessionSlotId,
6266
6279
  deriveShortAuthenticationString,
6267
6280
  encodeMessage,
6281
+ encodeMessageFailure,
6268
6282
  encodePairingQrPayload,
6269
6283
  encodeProtocolVersion,
6270
6284
  encodeRosterEntryForSigning,
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export { SecretstreamState, deriveSecretstreamPair, FrameTag, type SecretstreamD
9
9
  export { deriveShortAuthenticationString, type ShortAuthenticationString } from './crypto/sas';
10
10
  export { deriveSessionSlotId, derivePairingSlotId } from './crypto/slot';
11
11
  export type { JsonValue, BridgeMessage, HeartbeatMessage, CapabilityRequestMessage, CapabilityResponseMessage, CapabilityErrorCode, EventMessage } from './wire/messages';
12
- export { encodeMessage, decodeMessage, UnsupportedVerbError, isUnsupportedVerbError, UNSUPPORTED_VERB_ERROR_CODE, MAX_FRAME_LENGTH, MAX_DECODED_LENGTH, COMPRESSION_THRESHOLD, } from './wire/framing';
12
+ export { encodeMessage, encodeMessageFailure, decodeMessage, UnsupportedVerbError, isUnsupportedVerbError, UNSUPPORTED_VERB_ERROR_CODE, RESPONSE_TOO_LARGE_ERROR_CODE, MAX_FRAME_LENGTH, MAX_DECODED_LENGTH, COMPRESSION_THRESHOLD, } from './wire/framing';
13
13
  export { isJsonValue, isRecord } from './wire/json-value';
14
14
  export { SessionFrameKind, wrapSessionFrame, unwrapSessionFrame } from './wire/session-frame';
15
15
  export { parseCapabilityRequestPayload, parseReadStreamResponsePayload, parseReadBoardResponsePayload, parseReadDiffResponsePayload, parseTranscriptWindowResponsePayload, parseRegisterPushRequestPayload, parseStartSessionResponsePayload, parsePauseSessionResponsePayload, isReadStreamSessionStatusWire, type CapabilityRequestPayloadMap, type CapabilityResponsePayloadMap, type ReadStreamRequestPayload, type ReadStreamResponsePayload, type ReadStreamSessionStatusWire, type TranscriptWindowResponsePayload, type ReadBoardRequestPayload, type ReadBoardView, type ReadBoardProjectSummary, type ReadBoardProjectGroup, type ReadBoardProjectListResponsePayload, type ReadBoardSnapshotResponsePayload, type ReadBoardArchivedResponsePayload, type ReadBoardResponsePayload, type ReadDiffScope, type ReadDiffRequestPayload, type ReadDiffResponsePayload, type SendUserMessageRequestPayload, type SendUserMessageResponsePayload, type MoveTaskRequestPayload, type MoveTaskResponsePayload, type AnswerPermissionPromptRequestPayload, type AnswerPermissionPromptResponsePayload, type InteractiveTerminalRequestPayload, type InteractiveTerminalResponsePayload, type BoardToolRequestPayload, type BoardToolResponsePayload, type RegisterPushRequestPayload, type RegisterPushResponsePayload, type StartSessionRequestPayload, type StartSessionResponsePayload, type StartSessionOutcome, type PauseSessionRequestPayload, type PauseSessionResponsePayload, } from './wire/payloads';
package/dist/index.mjs CHANGED
@@ -4883,6 +4883,7 @@ var FRAME_FORMAT_DEFLATE = 1;
4883
4883
  var JSON_OPEN_BRACE = 123;
4884
4884
  var DEFLATE_HEADER_LENGTH = 5;
4885
4885
  var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
4886
+ var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
4886
4887
  var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
4887
4888
  var UnsupportedVerbError = class extends Error {
4888
4889
  requestId;
@@ -4919,6 +4920,15 @@ function encodeMessage(message) {
4919
4920
  }
4920
4921
  return frame;
4921
4922
  }
4923
+ function encodeMessageFailure(message) {
4924
+ try {
4925
+ if (new TextEncoder().encode(JSON.stringify(message)).length <= MAX_FRAME_LENGTH) return null;
4926
+ encodeMessage(message);
4927
+ return null;
4928
+ } catch (error) {
4929
+ return error instanceof Error ? error.message : String(error);
4930
+ }
4931
+ }
4922
4932
  function decodeMessage(bytes) {
4923
4933
  if (bytes.length > MAX_FRAME_LENGTH) {
4924
4934
  throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
@@ -6118,6 +6128,7 @@ export {
6118
6128
  PUSH_CATEGORIES,
6119
6129
  PUSH_ENVELOPE_MAX_AGE_MS,
6120
6130
  PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
6131
+ RESPONSE_TOO_LARGE_ERROR_CODE,
6121
6132
  SecretstreamState,
6122
6133
  SessionFrameKind,
6123
6134
  UNSUPPORTED_VERB_ERROR_CODE,
@@ -6137,6 +6148,7 @@ export {
6137
6148
  deriveSessionSlotId,
6138
6149
  deriveShortAuthenticationString,
6139
6150
  encodeMessage,
6151
+ encodeMessageFailure,
6140
6152
  encodePairingQrPayload,
6141
6153
  encodeProtocolVersion,
6142
6154
  encodeRosterEntryForSigning,
@@ -6,6 +6,8 @@ export declare const MAX_DECODED_LENGTH: number;
6
6
  export declare const COMPRESSION_THRESHOLD: number;
7
7
  /** The `code` a desktop puts on its refusal of a verb its build does not know. */
8
8
  export declare const UNSUPPORTED_VERB_ERROR_CODE: CapabilityErrorCode;
9
+ /** The `code` a desktop puts on its refusal of a response that would not fit the frame caps above. */
10
+ export declare const RESPONSE_TOO_LARGE_ERROR_CODE: CapabilityErrorCode;
9
11
  /**
10
12
  * Thrown by `decodeMessage` for a capability-request whose envelope is
11
13
  * well-formed (a string `requestId`, a string `verb`, a JSON `payload`) but
@@ -34,4 +36,17 @@ export declare class UnsupportedVerbError extends Error {
34
36
  */
35
37
  export declare function isUnsupportedVerbError(error: unknown): error is UnsupportedVerbError;
36
38
  export declare function encodeMessage(message: BridgeMessage): Uint8Array;
39
+ /**
40
+ * Why `encodeMessage` would throw for `message`, or null when it would not,
41
+ * without keeping the frame. A sender with side effects asks this BEFORE
42
+ * committing them, so it can send something smaller instead of committing to
43
+ * a message it cannot send (the desktop re-takes a shorter terminal seed).
44
+ *
45
+ * Exact, not an estimate. Raw UTF-8 JSON at or under MAX_FRAME_LENGTH always
46
+ * fits, because `encodeMessage` only switches to deflate when deflate is
47
+ * smaller, so that common case costs one stringify. A larger message runs the
48
+ * real encode, deflate included, and a sender that then sends it pays that
49
+ * encode a second time. Measured terminal seeds are far under the threshold.
50
+ */
51
+ export declare function encodeMessageFailure(message: BridgeMessage): string | null;
37
52
  export declare function decodeMessage(bytes: Uint8Array): BridgeMessage;
@@ -32,13 +32,21 @@ export interface CapabilityRequestMessage {
32
32
  }
33
33
  /**
34
34
  * A stable key a client can branch on, beside the human-readable `error`.
35
- * Today the one member is the refusal a desktop sends for a verb its build
36
- * does not know (see `UnsupportedVerbError` in framing.ts), so a newer phone
37
- * can tell "desktop is old" from "desktop is unreachable" and show the right
38
- * copy. A union rather than a bare string so a second code is a deliberate
39
- * addition here, not a typo at a call site.
35
+ * A union rather than a bare string so a new code is a deliberate addition
36
+ * here, not a typo at a call site.
37
+ *
38
+ * - `unsupported-verb`: the desktop's build does not know the verb (see
39
+ * `UnsupportedVerbError` in framing.ts), so a newer phone can tell "desktop
40
+ * is old" from "desktop is unreachable" and show the right copy.
41
+ * - `response-too-large`: the answer would not fit the frame caps
42
+ * (MAX_DECODED_LENGTH before compression, MAX_FRAME_LENGTH after), so the
43
+ * desktop refused instead of sending it. The request was valid and its
44
+ * target still exists, so a client must not read it as the target being
45
+ * gone (a session ending, a project removed). It is a backstop. A desktop
46
+ * shrinks a `read-stream` terminal seed to fit rather than refuse it, so a
47
+ * phone's terminal subscribe is never answered with this code.
40
48
  */
41
- export type CapabilityErrorCode = 'unsupported-verb';
49
+ export type CapabilityErrorCode = 'unsupported-verb' | 'response-too-large';
42
50
  export interface CapabilityResponseMessage {
43
51
  type: 'capability-response';
44
52
  requestId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangentic/protocol",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Kangentic mobile bridge wire protocol: framing, Noise-based pairing and session handshakes, signed device roster, and capability verbs shared between desktop and mobile.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",