@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/capabilities/verbs.d.ts +1 -1
- package/dist/crypto/push-envelope.d.ts +38 -0
- package/dist/events/event.d.ts +26 -4
- package/dist/events/payloads.d.ts +54 -1
- package/dist/index.cjs +1028 -76
- package/dist/index.d.ts +5 -4
- package/dist/index.mjs +1006 -76
- package/dist/version.d.ts +1 -1
- package/dist/wire/framing.d.ts +4 -0
- package/dist/wire/payloads.d.ts +103 -7
- package/package.json +3 -2
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* wire a handler, and a device's roster entry still has to include it in
|
|
10
10
|
* its CapabilitySet.
|
|
11
11
|
*/
|
|
12
|
-
export declare const CAPABILITY_VERBS: readonly ["read-stream", "read-board", "read-diff", "send-user-message", "move-task", "answer-permission-prompt", "interactive-terminal", "board-tool-read", "board-tool-write"];
|
|
12
|
+
export declare const CAPABILITY_VERBS: readonly ["read-stream", "read-board", "read-diff", "send-user-message", "move-task", "answer-permission-prompt", "interactive-terminal", "board-tool-read", "board-tool-write", "register-push"];
|
|
13
13
|
export type CapabilityVerb = (typeof CAPABILITY_VERBS)[number];
|
|
14
14
|
export declare function isCapabilityVerb(value: string): value is CapabilityVerb;
|
|
15
15
|
/** Deny-by-default: a verb absent from the set is not authorized. */
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const PUSH_CATEGORIES: readonly ["permission-needed", "agent-question", "turn-complete", "session-failed"];
|
|
2
|
+
export type PushCategory = (typeof PUSH_CATEGORIES)[number];
|
|
3
|
+
export declare function isPushCategory(value: unknown): value is PushCategory;
|
|
4
|
+
/** Reject an opened envelope whose sentAt is older than this (stale replay). */
|
|
5
|
+
export declare const PUSH_ENVELOPE_MAX_AGE_MS: number;
|
|
6
|
+
/** Tolerated forward clock skew; anything claiming to be further in the future is rejected. */
|
|
7
|
+
export declare const PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS: number;
|
|
8
|
+
/**
|
|
9
|
+
* The real notification content, visible only on the sealing desktop and
|
|
10
|
+
* the opening device. Everything the OS-visible push payload carries
|
|
11
|
+
* instead is a static placeholder.
|
|
12
|
+
*/
|
|
13
|
+
export interface PushEnvelopePlaintext {
|
|
14
|
+
category: PushCategory;
|
|
15
|
+
projectId: string;
|
|
16
|
+
taskId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
taskTitle: string;
|
|
19
|
+
detail: string;
|
|
20
|
+
/** Wall-clock ms at seal time; openPushEnvelope enforces the freshness window from it. */
|
|
21
|
+
sentAt: number;
|
|
22
|
+
}
|
|
23
|
+
/** Narrows a decrypted envelope's JSON to PushEnvelopePlaintext. Throws on any malformed field. */
|
|
24
|
+
export declare function parsePushEnvelopePlaintext(value: unknown): PushEnvelopePlaintext;
|
|
25
|
+
/**
|
|
26
|
+
* Seals a notification into an opaque base64url blob:
|
|
27
|
+
* base64url(nonce[24] || XChaCha20-Poly1305(pushKey, nonce, aad=recipientStaticPublicKey, JSON(plaintext))).
|
|
28
|
+
*/
|
|
29
|
+
export declare function sealPushEnvelope(pushKey: Uint8Array, recipientStaticPublicKey: Uint8Array, plaintext: PushEnvelopePlaintext): string;
|
|
30
|
+
/**
|
|
31
|
+
* Opens a sealed push blob. Throws on: a malformed blob, a tampered
|
|
32
|
+
* ciphertext, the wrong key, the wrong recipient key (AAD mismatch),
|
|
33
|
+
* non-JSON or malformed plaintext, an unknown category, or a sentAt
|
|
34
|
+
* outside the freshness window (older than 24h, or more than 5min in the
|
|
35
|
+
* future). The caller (the device-side notification handler) catches and
|
|
36
|
+
* shows the generic placeholder.
|
|
37
|
+
*/
|
|
38
|
+
export declare function openPushEnvelope(pushKey: Uint8Array, recipientStaticPublicKey: Uint8Array, blob: string): PushEnvelopePlaintext;
|
package/dist/events/event.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The event contract the phone consumes, pushed as EventMessage.event over
|
|
3
3
|
* an established BridgeSession once Phase 2's capability handlers (desktop
|
|
4
4
|
* bridge module) subscribe SessionManager/transcript-service/DiffService to
|
|
5
|
-
* a session's live state.
|
|
5
|
+
* a session's live state. Six kinds:
|
|
6
6
|
*
|
|
7
7
|
* - transcript: a revision-gated delta from resolveTaskTranscript, for the
|
|
8
8
|
* phone's transcript-styled conversation view.
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* - terminal: raw PTY output from the unfiltered data-tap, for the phone's
|
|
14
14
|
* raw-terminal-mirror view (a distinct consumer from the parsed
|
|
15
15
|
* transcript).
|
|
16
|
+
* - terminal-resize: the PTY grid changed (desktop pane refit, phone fit
|
|
17
|
+
* request, or restore-on-release); the phone re-sizes its renderer to
|
|
18
|
+
* match before the TUI's repaint bytes arrive on the terminal stream.
|
|
16
19
|
* - board: a board-mutation notification, filterable by project and
|
|
17
20
|
* (optionally) task.
|
|
18
21
|
* - diff: a payload-less "something under this task's worktree changed,
|
|
@@ -24,12 +27,13 @@
|
|
|
24
27
|
* event before its router dispatches on `kind`.
|
|
25
28
|
*/
|
|
26
29
|
import type { JsonValue } from '../wire/messages';
|
|
27
|
-
import { type ActivityReasonWire, type ActivityStateWire, type SessionEventWire, type SessionUsageWire, type
|
|
30
|
+
import { type ActivityReasonWire, type ActivityStateWire, type SessionEventWire, type SessionUsageWire, type TerminalDimensionsWire, type TranscriptEventPayload } from './payloads';
|
|
28
31
|
export interface TranscriptEvent {
|
|
29
32
|
kind: 'transcript';
|
|
30
33
|
sessionId: string;
|
|
31
34
|
taskId: string;
|
|
32
|
-
|
|
35
|
+
/** Incremental (protocol v2): indexed upserts or a reset signal, never the whole transcript. See TranscriptEventPayload. */
|
|
36
|
+
payload: TranscriptEventPayload;
|
|
33
37
|
}
|
|
34
38
|
export type ActivityEventPayload = {
|
|
35
39
|
type: 'activity';
|
|
@@ -45,6 +49,17 @@ export type ActivityEventPayload = {
|
|
|
45
49
|
type: 'permission';
|
|
46
50
|
promptId: string;
|
|
47
51
|
pending: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The streamed session's PTY exited. Pushed once by the desktop's
|
|
55
|
+
* read-stream subscription right before it tears itself down, so the
|
|
56
|
+
* phone learns "this session is over" from the feed itself instead of
|
|
57
|
+
* inferring it from silence. `intentional` distinguishes a deliberate
|
|
58
|
+
* stop (desktop Stop button, suspend, shutdown) from a crash.
|
|
59
|
+
*/
|
|
60
|
+
| {
|
|
61
|
+
type: 'session-ended';
|
|
62
|
+
intentional: boolean;
|
|
48
63
|
};
|
|
49
64
|
export interface ActivityEvent {
|
|
50
65
|
kind: 'activity';
|
|
@@ -60,6 +75,13 @@ export interface TerminalEvent {
|
|
|
60
75
|
data: string;
|
|
61
76
|
};
|
|
62
77
|
}
|
|
78
|
+
export interface TerminalResizeEvent {
|
|
79
|
+
kind: 'terminal-resize';
|
|
80
|
+
sessionId: string;
|
|
81
|
+
taskId: string;
|
|
82
|
+
/** The PTY's new grid; every terminal byte sent after this event is laid out for it. */
|
|
83
|
+
payload: TerminalDimensionsWire;
|
|
84
|
+
}
|
|
63
85
|
export interface BoardEventPayload {
|
|
64
86
|
change: 'task-created' | 'task-updated' | 'task-deleted' | 'swimlane-updated' | 'backlog-changed';
|
|
65
87
|
ids: string[];
|
|
@@ -75,7 +97,7 @@ export interface DiffEvent {
|
|
|
75
97
|
taskId: string;
|
|
76
98
|
payload: null;
|
|
77
99
|
}
|
|
78
|
-
export type BridgeEvent = TranscriptEvent | ActivityEvent | TerminalEvent | BoardEvent | DiffEvent;
|
|
100
|
+
export type BridgeEvent = TranscriptEvent | ActivityEvent | TerminalEvent | TerminalResizeEvent | BoardEvent | DiffEvent;
|
|
79
101
|
/**
|
|
80
102
|
* Narrows an activity event's payload to its typed union. Throws on a
|
|
81
103
|
* malformed required field so the caller can drop the event cleanly.
|
|
@@ -68,6 +68,51 @@ export type TranscriptEntryWire = {
|
|
|
68
68
|
subtype: TranscriptSystemSubtypeWire;
|
|
69
69
|
text: string;
|
|
70
70
|
};
|
|
71
|
+
/** One transcript entry at its absolute position in the full transcript at the delta's revision. */
|
|
72
|
+
export interface TranscriptUpsertWire {
|
|
73
|
+
index: number;
|
|
74
|
+
entry: TranscriptEntryWire;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Protocol v2 transcript payload: incremental, never the whole transcript.
|
|
78
|
+
*
|
|
79
|
+
* - 'delta' carries only the entries that changed or appeared since the
|
|
80
|
+
* last delta the desktop sent on this subscription, as absolute-indexed
|
|
81
|
+
* upserts in ascending order. A large delta is split across several
|
|
82
|
+
* events (same revision) so every frame stays well under the transport
|
|
83
|
+
* caps and the first chunk renders without waiting for the rest. The
|
|
84
|
+
* phone applies upserts inside or adjacent to its loaded window and
|
|
85
|
+
* re-requests a window when it detects a gap.
|
|
86
|
+
* - 'reset' tells the phone its local state is unreconstructable from
|
|
87
|
+
* deltas (entries removed or reordered, or a degraded transcript source
|
|
88
|
+
* whose uuids are unstable) - drop local entries and re-request a
|
|
89
|
+
* window via the read-stream 'transcript-window' action.
|
|
90
|
+
*
|
|
91
|
+
* `revision` is the desktop's monotonically increasing whole-transcript
|
|
92
|
+
* version; `totalEntries` is the full transcript length at that revision,
|
|
93
|
+
* which is how the phone knows history extends above its window.
|
|
94
|
+
*/
|
|
95
|
+
export type TranscriptEventPayload = {
|
|
96
|
+
mode: 'delta';
|
|
97
|
+
revision: number;
|
|
98
|
+
totalEntries: number;
|
|
99
|
+
upserts: TranscriptUpsertWire[];
|
|
100
|
+
} | {
|
|
101
|
+
mode: 'reset';
|
|
102
|
+
revision: number;
|
|
103
|
+
totalEntries: number;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* PTY grid dimensions, shared by the read-stream snapshot (`ptyDimensions`),
|
|
107
|
+
* the `terminal-resize` event, and the interactive-terminal `resize` action.
|
|
108
|
+
* The bytes on the terminal stream are laid out for exactly this grid; a
|
|
109
|
+
* renderer that uses any other geometry misplaces every cursor-addressed
|
|
110
|
+
* frame the fullscreen TUI draws.
|
|
111
|
+
*/
|
|
112
|
+
export interface TerminalDimensionsWire {
|
|
113
|
+
cols: number;
|
|
114
|
+
rows: number;
|
|
115
|
+
}
|
|
71
116
|
/** Mirrors the desktop's ActivityState. 'permission' means the agent paused for user approval (incl. AskUserQuestion / ExitPlanMode pauses). */
|
|
72
117
|
export type ActivityStateWire = 'thinking' | 'idle' | 'permission';
|
|
73
118
|
/** Mirrors the desktop's ActivityReason discriminated union. */
|
|
@@ -198,8 +243,16 @@ export interface DiffFileContentWire {
|
|
|
198
243
|
modified: string;
|
|
199
244
|
language: string;
|
|
200
245
|
}
|
|
201
|
-
/** Narrows a transcript
|
|
246
|
+
/** Narrows a transcript entry array (windowed-history pages). Throws on a malformed entry so the caller can drop the payload. */
|
|
202
247
|
export declare function parseTranscriptEntriesWire(payload: JsonValue): TranscriptEntryWire[];
|
|
248
|
+
/** Narrows a transcript event's delta/reset payload. Throws on a malformed required field so the caller can drop the push. */
|
|
249
|
+
export declare function parseTranscriptEventPayload(payload: JsonValue): TranscriptEventPayload;
|
|
250
|
+
/**
|
|
251
|
+
* Narrows a PTY dimensions payload. Bounds are sanity caps, not policy:
|
|
252
|
+
* the desktop's own resize clamp (cols >= 2, rows >= 1) is the floor, and
|
|
253
|
+
* 1000 on each axis rejects garbage without constraining any real device.
|
|
254
|
+
*/
|
|
255
|
+
export declare function parseTerminalDimensionsWire(payload: JsonValue): TerminalDimensionsWire;
|
|
203
256
|
/** True for a well-formed ActivityStateWire value. */
|
|
204
257
|
export declare function isActivityStateWire(value: unknown): value is ActivityStateWire;
|
|
205
258
|
/** True for a well-formed ActivityReasonWire value. */
|