@kangentic/protocol 0.17.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/capabilities/verbs.d.ts +8 -2
- package/dist/events/payloads.d.ts +39 -3
- package/dist/index.cjs +36 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +33 -2
- package/dist/wire/framing.d.ts +15 -0
- package/dist/wire/messages.d.ts +14 -6
- package/dist/wire/payloads.d.ts +25 -0
- package/package.json +1 -1
|
@@ -16,9 +16,15 @@
|
|
|
16
16
|
* `start-session` is the second lifecycle verb beside `move-task`: it spawns
|
|
17
17
|
* or resumes a task's session in the column the task is already in, so a
|
|
18
18
|
* phone's "Session ended" state can bring the agent back without moving the
|
|
19
|
-
* task. A live session is an idempotent no-op;
|
|
19
|
+
* task. A live session is an idempotent no-op; `start-session` never stops
|
|
20
|
+
* anything.
|
|
21
|
+
*
|
|
22
|
+
* `pause-session` is the third lifecycle verb: it suspends a task's live
|
|
23
|
+
* session exactly as the desktop's Pause button does, without moving the task,
|
|
24
|
+
* and keeps the conversation for a later `start-session` Resume. Gate it on
|
|
25
|
+
* `BoardTaskWire.pausable`.
|
|
20
26
|
*/
|
|
21
|
-
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", "start-session"];
|
|
27
|
+
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", "start-session", "pause-session"];
|
|
22
28
|
export type CapabilityVerb = (typeof CAPABILITY_VERBS)[number];
|
|
23
29
|
export declare function isCapabilityVerb(value: string): value is CapabilityVerb;
|
|
24
30
|
/** Deny-by-default: a verb absent from the set is not authorized. */
|
|
@@ -409,9 +409,14 @@ export interface BoardTaskWire {
|
|
|
409
409
|
* a Command Terminal) and no live one. No column or archive gate applies. It
|
|
410
410
|
* is the fact the desktop card's "Paused" footer reads, so a paused task in
|
|
411
411
|
* Done or on the archived page reads true here while `resumable` reads
|
|
412
|
-
* false.
|
|
413
|
-
*
|
|
414
|
-
*
|
|
412
|
+
* false. The desktop draws an archived task as a compact card with no
|
|
413
|
+
* footer, so a client that matches the desktop card draws "Paused" only
|
|
414
|
+
* when `archived_at` is null. A move from another column into Done
|
|
415
|
+
* archives the task in the same step, so only a task that sits in Done
|
|
416
|
+
* unarchived gets the full card and its footer. A move into To Do removes
|
|
417
|
+
* the task's session rows, so a task there normally reads false. Gate
|
|
418
|
+
* Resume on `resumable` only; `resumable: true` always comes with
|
|
419
|
+
* `paused: true`.
|
|
415
420
|
*
|
|
416
421
|
* A desktop pause clears the task's `session_id`, so find a paused session
|
|
417
422
|
* from this field, not from `session_id`. A respawn queued behind the
|
|
@@ -429,6 +434,37 @@ export interface BoardTaskWire {
|
|
|
429
434
|
* the key from every real wire response.
|
|
430
435
|
*/
|
|
431
436
|
paused?: boolean | null;
|
|
437
|
+
/**
|
|
438
|
+
* True when the task has a live session (`running` or `queued`) and is not
|
|
439
|
+
* in To Do. It is the desktop's promise that `pause-session` for this task
|
|
440
|
+
* pauses that session exactly as its Pause button does: the conversation is
|
|
441
|
+
* kept for a later Resume, and the pause sticks, so a column with auto-spawn
|
|
442
|
+
* on does not start the task again by itself. Gate a phone's Pause control on
|
|
443
|
+
* THIS field.
|
|
444
|
+
*
|
|
445
|
+
* Done and archived do not clear it, matching the desktop, whose Pause stays
|
|
446
|
+
* available there because it is the only in-window stop for a live agent. A
|
|
447
|
+
* move into Done suspends the task's session in the same step, so a task
|
|
448
|
+
* there normally has no live session and reads false. It reads true only
|
|
449
|
+
* when that suspend failed and left the agent running.
|
|
450
|
+
*
|
|
451
|
+
* Never true while `paused` is true: a paused task has no live session. While
|
|
452
|
+
* `spawn_progress` is non-null and no session is live, the desktop card still
|
|
453
|
+
* shows a Pause control, but its click only cancels an in-flight resume or
|
|
454
|
+
* start, which `pause-session` does not promise. This field reads false
|
|
455
|
+
* then, and the verb refuses a task with no live session.
|
|
456
|
+
*
|
|
457
|
+
* A pause, a session's start and its end each reach the phone as a
|
|
458
|
+
* `task-updated` board event, after which the next snapshot carries the new
|
|
459
|
+
* value. Null or absent means a desktop that predates the field
|
|
460
|
+
* (pre-0.18.0), and a client offers no Pause then: a 0.15.0 to 0.17.0
|
|
461
|
+
* desktop refuses `pause-session` with the `unsupported-verb` code, and an
|
|
462
|
+
* older one does not answer it at all.
|
|
463
|
+
*
|
|
464
|
+
* Declared OPTIONAL (`?`) like `paused`; `parseBoardTaskWire` populates the
|
|
465
|
+
* key from every real wire response.
|
|
466
|
+
*/
|
|
467
|
+
pausable?: boolean | null;
|
|
432
468
|
}
|
|
433
469
|
/** Phone-needed subset of the desktop's BacklogTask row. */
|
|
434
470
|
export interface BacklogItemWire {
|
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,
|
|
@@ -90,6 +92,7 @@ __export(index_exports, {
|
|
|
90
92
|
parseCapabilityRequestPayload: () => parseCapabilityRequestPayload,
|
|
91
93
|
parseDiffFileContentWire: () => parseDiffFileContentWire,
|
|
92
94
|
parseDiffFileListWire: () => parseDiffFileListWire,
|
|
95
|
+
parsePauseSessionResponsePayload: () => parsePauseSessionResponsePayload,
|
|
93
96
|
parsePushEnvelopePlaintext: () => parsePushEnvelopePlaintext,
|
|
94
97
|
parseReadBoardResponsePayload: () => parseReadBoardResponsePayload,
|
|
95
98
|
parseReadDiffResponsePayload: () => parseReadDiffResponsePayload,
|
|
@@ -4971,7 +4974,8 @@ var CAPABILITY_VERBS = [
|
|
|
4971
4974
|
"board-tool-read",
|
|
4972
4975
|
"board-tool-write",
|
|
4973
4976
|
"register-push",
|
|
4974
|
-
"start-session"
|
|
4977
|
+
"start-session",
|
|
4978
|
+
"pause-session"
|
|
4975
4979
|
];
|
|
4976
4980
|
function isCapabilityVerb(value) {
|
|
4977
4981
|
return CAPABILITY_VERBS.includes(value);
|
|
@@ -5008,6 +5012,7 @@ var FRAME_FORMAT_DEFLATE = 1;
|
|
|
5008
5012
|
var JSON_OPEN_BRACE = 123;
|
|
5009
5013
|
var DEFLATE_HEADER_LENGTH = 5;
|
|
5010
5014
|
var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
|
|
5015
|
+
var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
|
|
5011
5016
|
var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
|
|
5012
5017
|
var UnsupportedVerbError = class extends Error {
|
|
5013
5018
|
requestId;
|
|
@@ -5044,6 +5049,15 @@ function encodeMessage(message) {
|
|
|
5044
5049
|
}
|
|
5045
5050
|
return frame;
|
|
5046
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
|
+
}
|
|
5047
5061
|
function decodeMessage(bytes) {
|
|
5048
5062
|
if (bytes.length > MAX_FRAME_LENGTH) {
|
|
5049
5063
|
throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
|
|
@@ -5466,7 +5480,8 @@ function parseBoardTaskWire(value) {
|
|
|
5466
5480
|
updated_at: requireString(value, "updated_at", "board task"),
|
|
5467
5481
|
spawn_progress: nullableString(value, "spawn_progress"),
|
|
5468
5482
|
resumable: nullableBoolean(value, "resumable"),
|
|
5469
|
-
paused: nullableBoolean(value, "paused")
|
|
5483
|
+
paused: nullableBoolean(value, "paused"),
|
|
5484
|
+
pausable: nullableBoolean(value, "pausable")
|
|
5470
5485
|
};
|
|
5471
5486
|
}
|
|
5472
5487
|
function parseBacklogItemWire(value) {
|
|
@@ -5778,6 +5793,20 @@ function parseStartSessionResponsePayload(payload) {
|
|
|
5778
5793
|
}
|
|
5779
5794
|
return { ok: payload.ok, outcome: payload.outcome };
|
|
5780
5795
|
}
|
|
5796
|
+
function parsePauseSessionRequestPayload(payload) {
|
|
5797
|
+
if (!isRecord(payload)) throw new Error("pause-session payload must be an object");
|
|
5798
|
+
if (typeof payload.taskId !== "string") throw new Error('pause-session payload missing "taskId"');
|
|
5799
|
+
if (typeof payload.projectId !== "string") throw new Error('pause-session payload missing "projectId"');
|
|
5800
|
+
return {
|
|
5801
|
+
taskId: payload.taskId,
|
|
5802
|
+
projectId: payload.projectId
|
|
5803
|
+
};
|
|
5804
|
+
}
|
|
5805
|
+
function parsePauseSessionResponsePayload(payload) {
|
|
5806
|
+
if (!isRecord(payload)) throw new Error("pause-session response must be an object");
|
|
5807
|
+
if (typeof payload.ok !== "boolean") throw new Error('pause-session response missing "ok"');
|
|
5808
|
+
return { ok: payload.ok };
|
|
5809
|
+
}
|
|
5781
5810
|
function parseAnswerPermissionPromptRequestPayload(payload) {
|
|
5782
5811
|
if (!isRecord(payload)) throw new Error("answer-permission-prompt payload must be an object");
|
|
5783
5812
|
if (typeof payload.sessionId !== "string") throw new Error('answer-permission-prompt payload missing "sessionId"');
|
|
@@ -5875,6 +5904,8 @@ function parseCapabilityRequestPayload(verb, payload) {
|
|
|
5875
5904
|
return parseRegisterPushRequestPayload(payload);
|
|
5876
5905
|
case "start-session":
|
|
5877
5906
|
return parseStartSessionRequestPayload(payload);
|
|
5907
|
+
case "pause-session":
|
|
5908
|
+
return parsePauseSessionRequestPayload(payload);
|
|
5878
5909
|
default: {
|
|
5879
5910
|
const exhaustiveCheck = verb;
|
|
5880
5911
|
throw new Error(`Unknown capability verb: ${String(exhaustiveCheck)}`);
|
|
@@ -6227,6 +6258,7 @@ function isBridgeEvent(value) {
|
|
|
6227
6258
|
PUSH_CATEGORIES,
|
|
6228
6259
|
PUSH_ENVELOPE_MAX_AGE_MS,
|
|
6229
6260
|
PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
|
|
6261
|
+
RESPONSE_TOO_LARGE_ERROR_CODE,
|
|
6230
6262
|
SecretstreamState,
|
|
6231
6263
|
SessionFrameKind,
|
|
6232
6264
|
UNSUPPORTED_VERB_ERROR_CODE,
|
|
@@ -6246,6 +6278,7 @@ function isBridgeEvent(value) {
|
|
|
6246
6278
|
deriveSessionSlotId,
|
|
6247
6279
|
deriveShortAuthenticationString,
|
|
6248
6280
|
encodeMessage,
|
|
6281
|
+
encodeMessageFailure,
|
|
6249
6282
|
encodePairingQrPayload,
|
|
6250
6283
|
encodeProtocolVersion,
|
|
6251
6284
|
encodeRosterEntryForSigning,
|
|
@@ -6278,6 +6311,7 @@ function isBridgeEvent(value) {
|
|
|
6278
6311
|
parseCapabilityRequestPayload,
|
|
6279
6312
|
parseDiffFileContentWire,
|
|
6280
6313
|
parseDiffFileListWire,
|
|
6314
|
+
parsePauseSessionResponsePayload,
|
|
6281
6315
|
parsePushEnvelopePlaintext,
|
|
6282
6316
|
parseReadBoardResponsePayload,
|
|
6283
6317
|
parseReadDiffResponsePayload,
|
package/dist/index.d.ts
CHANGED
|
@@ -9,10 +9,10 @@ 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
|
-
export { parseCapabilityRequestPayload, parseReadStreamResponsePayload, parseReadBoardResponsePayload, parseReadDiffResponsePayload, parseTranscriptWindowResponsePayload, parseRegisterPushRequestPayload, parseStartSessionResponsePayload, 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, } from './wire/payloads';
|
|
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';
|
|
16
16
|
export { PUSH_CATEGORIES, isPushCategory, parsePushEnvelopePlaintext, sealPushEnvelope, openPushEnvelope, PUSH_ENVELOPE_MAX_AGE_MS, PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS, type PushCategory, type PushEnvelopePlaintext, } from './crypto/push-envelope';
|
|
17
17
|
export { PAIRING_URI_SCHEME, encodePairingQrPayload, decodePairingQrPayload, type PairingQrPayload, } from './pairing/qr';
|
|
18
18
|
export { sealPairingConfirm, openPairingConfirm } from './pairing/confirm';
|
package/dist/index.mjs
CHANGED
|
@@ -4845,7 +4845,8 @@ var CAPABILITY_VERBS = [
|
|
|
4845
4845
|
"board-tool-read",
|
|
4846
4846
|
"board-tool-write",
|
|
4847
4847
|
"register-push",
|
|
4848
|
-
"start-session"
|
|
4848
|
+
"start-session",
|
|
4849
|
+
"pause-session"
|
|
4849
4850
|
];
|
|
4850
4851
|
function isCapabilityVerb(value) {
|
|
4851
4852
|
return CAPABILITY_VERBS.includes(value);
|
|
@@ -4882,6 +4883,7 @@ var FRAME_FORMAT_DEFLATE = 1;
|
|
|
4882
4883
|
var JSON_OPEN_BRACE = 123;
|
|
4883
4884
|
var DEFLATE_HEADER_LENGTH = 5;
|
|
4884
4885
|
var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
|
|
4886
|
+
var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
|
|
4885
4887
|
var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
|
|
4886
4888
|
var UnsupportedVerbError = class extends Error {
|
|
4887
4889
|
requestId;
|
|
@@ -4918,6 +4920,15 @@ function encodeMessage(message) {
|
|
|
4918
4920
|
}
|
|
4919
4921
|
return frame;
|
|
4920
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
|
+
}
|
|
4921
4932
|
function decodeMessage(bytes) {
|
|
4922
4933
|
if (bytes.length > MAX_FRAME_LENGTH) {
|
|
4923
4934
|
throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
|
|
@@ -5340,7 +5351,8 @@ function parseBoardTaskWire(value) {
|
|
|
5340
5351
|
updated_at: requireString(value, "updated_at", "board task"),
|
|
5341
5352
|
spawn_progress: nullableString(value, "spawn_progress"),
|
|
5342
5353
|
resumable: nullableBoolean(value, "resumable"),
|
|
5343
|
-
paused: nullableBoolean(value, "paused")
|
|
5354
|
+
paused: nullableBoolean(value, "paused"),
|
|
5355
|
+
pausable: nullableBoolean(value, "pausable")
|
|
5344
5356
|
};
|
|
5345
5357
|
}
|
|
5346
5358
|
function parseBacklogItemWire(value) {
|
|
@@ -5652,6 +5664,20 @@ function parseStartSessionResponsePayload(payload) {
|
|
|
5652
5664
|
}
|
|
5653
5665
|
return { ok: payload.ok, outcome: payload.outcome };
|
|
5654
5666
|
}
|
|
5667
|
+
function parsePauseSessionRequestPayload(payload) {
|
|
5668
|
+
if (!isRecord(payload)) throw new Error("pause-session payload must be an object");
|
|
5669
|
+
if (typeof payload.taskId !== "string") throw new Error('pause-session payload missing "taskId"');
|
|
5670
|
+
if (typeof payload.projectId !== "string") throw new Error('pause-session payload missing "projectId"');
|
|
5671
|
+
return {
|
|
5672
|
+
taskId: payload.taskId,
|
|
5673
|
+
projectId: payload.projectId
|
|
5674
|
+
};
|
|
5675
|
+
}
|
|
5676
|
+
function parsePauseSessionResponsePayload(payload) {
|
|
5677
|
+
if (!isRecord(payload)) throw new Error("pause-session response must be an object");
|
|
5678
|
+
if (typeof payload.ok !== "boolean") throw new Error('pause-session response missing "ok"');
|
|
5679
|
+
return { ok: payload.ok };
|
|
5680
|
+
}
|
|
5655
5681
|
function parseAnswerPermissionPromptRequestPayload(payload) {
|
|
5656
5682
|
if (!isRecord(payload)) throw new Error("answer-permission-prompt payload must be an object");
|
|
5657
5683
|
if (typeof payload.sessionId !== "string") throw new Error('answer-permission-prompt payload missing "sessionId"');
|
|
@@ -5749,6 +5775,8 @@ function parseCapabilityRequestPayload(verb, payload) {
|
|
|
5749
5775
|
return parseRegisterPushRequestPayload(payload);
|
|
5750
5776
|
case "start-session":
|
|
5751
5777
|
return parseStartSessionRequestPayload(payload);
|
|
5778
|
+
case "pause-session":
|
|
5779
|
+
return parsePauseSessionRequestPayload(payload);
|
|
5752
5780
|
default: {
|
|
5753
5781
|
const exhaustiveCheck = verb;
|
|
5754
5782
|
throw new Error(`Unknown capability verb: ${String(exhaustiveCheck)}`);
|
|
@@ -6100,6 +6128,7 @@ export {
|
|
|
6100
6128
|
PUSH_CATEGORIES,
|
|
6101
6129
|
PUSH_ENVELOPE_MAX_AGE_MS,
|
|
6102
6130
|
PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
|
|
6131
|
+
RESPONSE_TOO_LARGE_ERROR_CODE,
|
|
6103
6132
|
SecretstreamState,
|
|
6104
6133
|
SessionFrameKind,
|
|
6105
6134
|
UNSUPPORTED_VERB_ERROR_CODE,
|
|
@@ -6119,6 +6148,7 @@ export {
|
|
|
6119
6148
|
deriveSessionSlotId,
|
|
6120
6149
|
deriveShortAuthenticationString,
|
|
6121
6150
|
encodeMessage,
|
|
6151
|
+
encodeMessageFailure,
|
|
6122
6152
|
encodePairingQrPayload,
|
|
6123
6153
|
encodeProtocolVersion,
|
|
6124
6154
|
encodeRosterEntryForSigning,
|
|
@@ -6151,6 +6181,7 @@ export {
|
|
|
6151
6181
|
parseCapabilityRequestPayload,
|
|
6152
6182
|
parseDiffFileContentWire,
|
|
6153
6183
|
parseDiffFileListWire,
|
|
6184
|
+
parsePauseSessionResponsePayload,
|
|
6154
6185
|
parsePushEnvelopePlaintext,
|
|
6155
6186
|
parseReadBoardResponsePayload,
|
|
6156
6187
|
parseReadDiffResponsePayload,
|
package/dist/wire/framing.d.ts
CHANGED
|
@@ -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;
|
package/dist/wire/messages.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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/dist/wire/payloads.d.ts
CHANGED
|
@@ -342,6 +342,29 @@ export interface StartSessionResponsePayload {
|
|
|
342
342
|
}
|
|
343
343
|
/** Phone-side guard: narrows a decoded start-session response before the phone acts on it. */
|
|
344
344
|
export declare function parseStartSessionResponsePayload(payload: JsonValue): StartSessionResponsePayload;
|
|
345
|
+
/**
|
|
346
|
+
* Keyed by task, like `start-session`: the desktop resolves the task's live
|
|
347
|
+
* session itself (a pause clears the task's `session_id`, and a stale phone
|
|
348
|
+
* view may name a session that already ended). It suspends that session the
|
|
349
|
+
* way the desktop's Pause button does, in the column the task is already in.
|
|
350
|
+
*/
|
|
351
|
+
export interface PauseSessionRequestPayload {
|
|
352
|
+
taskId: string;
|
|
353
|
+
projectId: string;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* `ok` means the pause was ACCEPTED: the task had a live session, and the
|
|
357
|
+
* desktop has recorded it as paused by the user. The agent's own shutdown runs
|
|
358
|
+
* behind this response, because it can outlast the phone's per-verb budget,
|
|
359
|
+
* and its end reaches the phone as the board event that carries
|
|
360
|
+
* `paused: true`. A task with no live session is refused (`ok: false` on the
|
|
361
|
+
* capability response), so there is no idempotent shape to report here.
|
|
362
|
+
*/
|
|
363
|
+
export interface PauseSessionResponsePayload {
|
|
364
|
+
ok: boolean;
|
|
365
|
+
}
|
|
366
|
+
/** Phone-side guard: narrows a decoded pause-session response before the phone acts on it. */
|
|
367
|
+
export declare function parsePauseSessionResponsePayload(payload: JsonValue): PauseSessionResponsePayload;
|
|
345
368
|
export interface AnswerPermissionPromptRequestPayload {
|
|
346
369
|
sessionId: string;
|
|
347
370
|
/** The prompt id the phone believes is outstanding - the handler rejects a stale/replayed answer whose id no longer matches the live awaited prompt. */
|
|
@@ -428,6 +451,7 @@ export interface CapabilityRequestPayloadMap {
|
|
|
428
451
|
'board-tool-write': BoardToolRequestPayload;
|
|
429
452
|
'register-push': RegisterPushRequestPayload;
|
|
430
453
|
'start-session': StartSessionRequestPayload;
|
|
454
|
+
'pause-session': PauseSessionRequestPayload;
|
|
431
455
|
}
|
|
432
456
|
export interface CapabilityResponsePayloadMap {
|
|
433
457
|
'read-stream': ReadStreamResponsePayload | TranscriptWindowResponsePayload;
|
|
@@ -441,6 +465,7 @@ export interface CapabilityResponsePayloadMap {
|
|
|
441
465
|
'board-tool-write': BoardToolResponsePayload;
|
|
442
466
|
'register-push': RegisterPushResponsePayload;
|
|
443
467
|
'start-session': StartSessionResponsePayload;
|
|
468
|
+
'pause-session': PauseSessionResponsePayload;
|
|
444
469
|
}
|
|
445
470
|
/**
|
|
446
471
|
* 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.
|
|
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",
|