@kangentic/protocol 0.2.0 → 0.5.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/version.d.ts CHANGED
@@ -8,5 +8,5 @@
8
8
  * differing prologue produces a differing transcript hash `h` from the
9
9
  * first MixHash call, which cascades into every derived key.
10
10
  */
11
- export declare const PROTOCOL_VERSION = "1";
11
+ export declare const PROTOCOL_VERSION = "2";
12
12
  export declare function encodeProtocolVersion(version?: string): Uint8Array;
@@ -1,4 +1,8 @@
1
1
  import type { BridgeMessage } from './messages';
2
2
  export declare const MAX_FRAME_LENGTH: number;
3
+ /** Upper bound on the JSON a compressed frame may declare/inflate to. */
4
+ export declare const MAX_DECODED_LENGTH: number;
5
+ /** JSON below this size ships raw - deflate overhead is not worth it. */
6
+ export declare const COMPRESSION_THRESHOLD: number;
3
7
  export declare function encodeMessage(message: BridgeMessage): Uint8Array;
4
8
  export declare function decodeMessage(bytes: Uint8Array): BridgeMessage;
@@ -15,11 +15,29 @@
15
15
  */
16
16
  import type { CapabilityVerb } from '../capabilities/verbs';
17
17
  import type { JsonValue } from './messages';
18
- import { type ActivityReasonWire, type ActivityStateWire, type BacklogItemWire, type BoardColumnWire, type BoardTaskWire, type DiffFileContentWire, type DiffFileListWire, type SessionUsageWire } from '../events/payloads';
18
+ import { type ActivityReasonWire, type ActivityStateWire, type BacklogItemWire, type BoardColumnWire, type BoardTaskWire, type DiffFileContentWire, type DiffFileListWire, type SessionUsageWire, type TerminalDimensionsWire, type TranscriptEntryWire } from '../events/payloads';
19
19
  export interface ReadStreamRequestPayload {
20
20
  sessionId: string;
21
- action: 'subscribe' | 'unsubscribe';
21
+ /**
22
+ * 'transcript-window' is a one-shot windowed-history read: the newest
23
+ * `limit` transcript entries strictly before `beforeIndex` (or the tail
24
+ * when `beforeIndex` is omitted). The desktop may return fewer entries
25
+ * than `limit` to keep the response frame small - page again from the
26
+ * returned `startIndex`. Live updates after 'subscribe' arrive as
27
+ * incremental TranscriptEvent deltas, never full transcripts.
28
+ */
29
+ action: 'subscribe' | 'unsubscribe' | 'transcript-window';
30
+ /** transcript-window only: fetch entries strictly before this absolute index. Omit for the newest window. */
31
+ beforeIndex?: number;
32
+ /** transcript-window only: maximum entries wanted (the desktop may cap this and may return fewer). */
33
+ limit?: number;
22
34
  }
35
+ /**
36
+ * Mirrors the desktop's SessionStatus. 'suspended' is a registered-but-
37
+ * parked session (resumable placeholder); 'exited' can appear when a
38
+ * snapshot races the session's teardown.
39
+ */
40
+ export type ReadStreamSessionStatusWire = 'running' | 'queued' | 'suspended' | 'exited';
23
41
  /** Initial snapshot returned on subscribe; live updates arrive as TerminalEvent/ActivityEvent/TranscriptEvent. */
24
42
  export interface ReadStreamResponsePayload {
25
43
  scrollback: string;
@@ -30,9 +48,33 @@ export interface ReadStreamResponsePayload {
30
48
  usage: SessionUsageWire | null;
31
49
  /** The live outstanding permission-prompt id (see answer-permission-prompt), or null when none is pending. */
32
50
  awaitedPromptId: string | null;
51
+ /**
52
+ * The PTY grid the scrollback bytes are laid out for. Absent from
53
+ * pre-0.4.0 desktops; the phone then falls back to inferring a width
54
+ * from the scrollback content.
55
+ */
56
+ ptyDimensions?: TerminalDimensionsWire;
57
+ /**
58
+ * The session's lifecycle status at snapshot time. Absent from
59
+ * pre-0.5.0 desktops; the phone then assumes 'running'.
60
+ */
61
+ sessionStatus?: ReadStreamSessionStatusWire;
33
62
  }
34
63
  /** Phone-side narrowing of a read-stream subscribe response. Throws on a malformed required field. */
35
64
  export declare function parseReadStreamResponsePayload(payload: JsonValue): ReadStreamResponsePayload;
65
+ /**
66
+ * A contiguous slice of the transcript, returned by the read-stream
67
+ * 'transcript-window' action. `startIndex` is the absolute index of
68
+ * `entries[0]`; `startIndex > 0` means more history exists above.
69
+ */
70
+ export interface TranscriptWindowResponsePayload {
71
+ revision: number;
72
+ totalEntries: number;
73
+ startIndex: number;
74
+ entries: TranscriptEntryWire[];
75
+ }
76
+ /** Phone-side narrowing of a transcript-window response. Throws on a malformed required field. */
77
+ export declare function parseTranscriptWindowResponsePayload(payload: JsonValue): TranscriptWindowResponsePayload;
36
78
  export interface ReadBoardRequestPayload {
37
79
  projectId?: string;
38
80
  /** Defaults to 'subscribe' when omitted. 'unsubscribe' only has an effect when projectId is set - the no-projectId project list is a one-shot read with no live feed to tear down. */
@@ -41,6 +83,13 @@ export interface ReadBoardRequestPayload {
41
83
  export interface ReadBoardProjectSummary {
42
84
  id: string;
43
85
  name: string;
86
+ /**
87
+ * Accent color for this project ("#rrggbb"). Today the desktop derives
88
+ * it deterministically from the project id; a user-set project color
89
+ * can later override it through this same field. Absent from pre-0.5.0
90
+ * desktops.
91
+ */
92
+ color?: string;
44
93
  }
45
94
  /** Returned when the request omits projectId - the phone's project-bootstrap listing. */
46
95
  export interface ReadBoardProjectListResponsePayload {
@@ -52,6 +101,8 @@ export interface ReadBoardSnapshotResponsePayload {
52
101
  columns: BoardColumnWire[];
53
102
  tasks: BoardTaskWire[];
54
103
  backlog: BacklogItemWire[];
104
+ /** The project's accent color ("#rrggbb"); same semantics as ReadBoardProjectSummary.color. Absent from pre-0.5.0 desktops. */
105
+ projectColor?: string;
55
106
  }
56
107
  export type ReadBoardResponsePayload = ReadBoardProjectListResponsePayload | ReadBoardSnapshotResponsePayload;
57
108
  /** Phone-side narrowing of a read-board response (project list or board snapshot). Throws on a malformed required field. */
@@ -95,13 +146,38 @@ export interface AnswerPermissionPromptRequestPayload {
95
146
  export interface AnswerPermissionPromptResponsePayload {
96
147
  answered: boolean;
97
148
  }
98
- export interface InteractiveTerminalRequestPayload {
149
+ /**
150
+ * Three actions ride the one interactive-terminal grant: a device trusted
151
+ * to type raw bytes into the PTY is equally trusted to resize it, so
152
+ * resize adds no new capability surface.
153
+ *
154
+ * - write (the default when `action` is omitted, which is what every
155
+ * pre-0.4.0 phone sends): raw bytes into the PTY.
156
+ * - resize: set the PTY grid so the TUI redraws phone-shaped. The desktop
157
+ * remembers its own last dimensions and restores them when the phone
158
+ * releases (explicitly, or implicitly on disconnect/revoke).
159
+ * - release-size: give the grid back to the desktop now.
160
+ */
161
+ export type InteractiveTerminalRequestPayload = {
99
162
  sessionId: string;
163
+ action?: 'write';
100
164
  data: string;
101
- }
102
- export interface InteractiveTerminalResponsePayload {
165
+ } | {
166
+ sessionId: string;
167
+ action: 'resize';
168
+ dimensions: TerminalDimensionsWire;
169
+ } | {
170
+ sessionId: string;
171
+ action: 'release-size';
172
+ };
173
+ export type InteractiveTerminalResponsePayload = {
103
174
  written: boolean;
104
- }
175
+ } | {
176
+ resized: boolean;
177
+ colsChanged: boolean;
178
+ } | {
179
+ released: boolean;
180
+ };
105
181
  export interface BoardToolRequestPayload {
106
182
  tool: string;
107
183
  params: JsonValue;
@@ -109,6 +185,24 @@ export interface BoardToolRequestPayload {
109
185
  export interface BoardToolResponsePayload {
110
186
  result: JsonValue;
111
187
  }
188
+ /**
189
+ * Registers (or unregisters) this device for E2E-encrypted push
190
+ * notifications. 'register' carries the device's Expo push token plus a
191
+ * device-generated 32-byte push key (base64url) the desktop seals every
192
+ * notification envelope with (see crypto/push-envelope.ts); 'unregister'
193
+ * carries neither. The desktop keys the registration by the requesting
194
+ * device's roster identity, never by anything in this payload.
195
+ */
196
+ export interface RegisterPushRequestPayload {
197
+ action: 'register' | 'unregister';
198
+ expoPushToken?: string;
199
+ pushKeyBase64?: string;
200
+ platform?: 'android' | 'ios';
201
+ }
202
+ export interface RegisterPushResponsePayload {
203
+ registered: boolean;
204
+ }
205
+ export declare function parseRegisterPushRequestPayload(payload: JsonValue): RegisterPushRequestPayload;
112
206
  export interface CapabilityRequestPayloadMap {
113
207
  'read-stream': ReadStreamRequestPayload;
114
208
  'read-board': ReadBoardRequestPayload;
@@ -119,9 +213,10 @@ export interface CapabilityRequestPayloadMap {
119
213
  'interactive-terminal': InteractiveTerminalRequestPayload;
120
214
  'board-tool-read': BoardToolRequestPayload;
121
215
  'board-tool-write': BoardToolRequestPayload;
216
+ 'register-push': RegisterPushRequestPayload;
122
217
  }
123
218
  export interface CapabilityResponsePayloadMap {
124
- 'read-stream': ReadStreamResponsePayload;
219
+ 'read-stream': ReadStreamResponsePayload | TranscriptWindowResponsePayload;
125
220
  'read-board': ReadBoardResponsePayload;
126
221
  'read-diff': ReadDiffResponsePayload;
127
222
  'send-user-message': SendUserMessageResponsePayload;
@@ -130,6 +225,7 @@ export interface CapabilityResponsePayloadMap {
130
225
  'interactive-terminal': InteractiveTerminalResponsePayload;
131
226
  'board-tool-read': BoardToolResponsePayload;
132
227
  'board-tool-write': BoardToolResponsePayload;
228
+ 'register-push': RegisterPushResponsePayload;
133
229
  }
134
230
  /**
135
231
  * Validates and narrows a decoded capability-request's generic JsonValue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangentic/protocol",
3
- "version": "0.2.0",
3
+ "version": "0.5.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",
@@ -42,7 +42,8 @@
42
42
  "dependencies": {
43
43
  "@noble/ciphers": "^2.2.0",
44
44
  "@noble/curves": "^2.2.0",
45
- "@noble/hashes": "^2.2.0"
45
+ "@noble/hashes": "^2.2.0",
46
+ "fflate": "^0.8.3"
46
47
  },
47
48
  "devDependencies": {
48
49
  "esbuild": "^0.28.0",