@kangentic/protocol 0.18.0 → 0.20.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/README.md +3 -1
- package/dist/capabilities/verbs.d.ts +9 -1
- package/dist/crypto/push-envelope.d.ts +12 -0
- package/dist/events/event.d.ts +20 -2
- package/dist/events/payloads.d.ts +35 -0
- package/dist/index.cjs +121 -9
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +109 -9
- package/dist/wire/framing.d.ts +27 -0
- package/dist/wire/messages.d.ts +30 -6
- package/dist/wire/payloads.d.ts +69 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,9 @@ model, and the honest relay-metadata statement.
|
|
|
25
25
|
phone: it is a liveness/intent signal, not the security boundary. It opens only if both peers
|
|
26
26
|
completed the same handshake transcript, which is exactly the property the SAS comparison
|
|
27
27
|
already vouches for - the human's comparison remains the actual defense.
|
|
28
|
-
- No shell, file, or arbitrary-command verb exists in the protocol.
|
|
28
|
+
- No shell, file, or arbitrary-command verb exists in the protocol. `command-terminal` starts the
|
|
29
|
+
project's agent CLI at the project root with no command, branch, or model named by the phone, and
|
|
30
|
+
the desktop ends the session when that agent exits.
|
|
29
31
|
|
|
30
32
|
This package has not undergone a third-party security audit. Report suspected vulnerabilities
|
|
31
33
|
per the repository's security policy rather than filing a public issue.
|
|
@@ -23,8 +23,16 @@
|
|
|
23
23
|
* session exactly as the desktop's Pause button does, without moving the task,
|
|
24
24
|
* and keeps the conversation for a later `start-session` Resume. Gate it on
|
|
25
25
|
* `BoardTaskWire.pausable`.
|
|
26
|
+
*
|
|
27
|
+
* `command-terminal` starts or stops a Command Terminal: an AGENT CLI the
|
|
28
|
+
* desktop launches at a project's root, the same session the desktop's
|
|
29
|
+
* Ctrl+Shift+P terminal runs. It is not a shell verb. The phone names no
|
|
30
|
+
* command, branch, model or effort, and the desktop ends the session when its
|
|
31
|
+
* agent exits, so no bare shell is left behind for `interactive-terminal` to
|
|
32
|
+
* reach. Stop accepts only a Command Terminal's session. Gate the phone's
|
|
33
|
+
* Terminals button on `ReadBoardSnapshotResponsePayload.commandTerminalsOffered`.
|
|
26
34
|
*/
|
|
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"];
|
|
35
|
+
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", "command-terminal"];
|
|
28
36
|
export type CapabilityVerb = (typeof CAPABILITY_VERBS)[number];
|
|
29
37
|
export declare function isCapabilityVerb(value: string): value is CapabilityVerb;
|
|
30
38
|
/** Deny-by-default: a verb absent from the set is not authorized. */
|
|
@@ -5,6 +5,15 @@ export declare function isPushCategory(value: unknown): value is PushCategory;
|
|
|
5
5
|
export declare const PUSH_ENVELOPE_MAX_AGE_MS: number;
|
|
6
6
|
/** Tolerated forward clock skew; anything claiming to be further in the future is rejected. */
|
|
7
7
|
export declare const PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS: number;
|
|
8
|
+
/**
|
|
9
|
+
* What kind of session a notification is about, when it is not a task's
|
|
10
|
+
* agent (protocol 0.20.0). `command-terminal`: a Command Terminal, so the tap
|
|
11
|
+
* opens that terminal's screen by `sessionId` (its `taskId` names no board
|
|
12
|
+
* task) and `taskTitle` carries the terminal's name.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PUSH_SESSION_KINDS: readonly ["command-terminal"];
|
|
15
|
+
export type PushSessionKind = (typeof PUSH_SESSION_KINDS)[number];
|
|
16
|
+
export declare function isPushSessionKind(value: unknown): value is PushSessionKind;
|
|
8
17
|
/**
|
|
9
18
|
* The real notification content, visible only on the sealing desktop and
|
|
10
19
|
* the opening device. Everything the OS-visible push payload carries
|
|
@@ -15,10 +24,13 @@ export interface PushEnvelopePlaintext {
|
|
|
15
24
|
projectId: string;
|
|
16
25
|
taskId: string;
|
|
17
26
|
sessionId: string;
|
|
27
|
+
/** The task's title, or for a `command-terminal` session the terminal's name. */
|
|
18
28
|
taskTitle: string;
|
|
19
29
|
detail: string;
|
|
20
30
|
/** Wall-clock ms at seal time; openPushEnvelope enforces the freshness window from it. */
|
|
21
31
|
sentAt: number;
|
|
32
|
+
/** Absent for a task's agent. See PUSH_SESSION_KINDS. */
|
|
33
|
+
sessionKind?: PushSessionKind;
|
|
22
34
|
}
|
|
23
35
|
/** Narrows a decrypted envelope's JSON to PushEnvelopePlaintext. Throws on any malformed field. */
|
|
24
36
|
export declare function parsePushEnvelopePlaintext(value: unknown): PushEnvelopePlaintext;
|
package/dist/events/event.d.ts
CHANGED
|
@@ -198,8 +198,20 @@ export interface TerminalResizeEvent {
|
|
|
198
198
|
/** The PTY's new grid; every terminal byte sent after this event is laid out for it. */
|
|
199
199
|
payload: TerminalDimensionsWire;
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* `command-terminals-changed` (protocol 0.20.0): one of the project's Command
|
|
203
|
+
* Terminals started, ended, or was named. `ids` holds the terminal's session id
|
|
204
|
+
* and the envelope carries no `taskId`, since a Command Terminal belongs to no
|
|
205
|
+
* task. The phone re-reads `commandTerminals` from the next snapshot. A change
|
|
206
|
+
* of activity does not fire it: the terminal's state and reason ride its own
|
|
207
|
+
* `read-stream` activity events, as a task agent's do, and the snapshot's
|
|
208
|
+
* `CommandTerminalWire.activity` covers the first paint.
|
|
209
|
+
*
|
|
210
|
+
* The union is closed, so a phone built before a kind existed drops that event
|
|
211
|
+
* (`isBridgeEvent` returns false) and keeps the rest of its feed.
|
|
212
|
+
*/
|
|
201
213
|
export interface BoardEventPayload {
|
|
202
|
-
change: 'task-created' | 'task-updated' | 'task-deleted' | 'swimlane-updated' | 'backlog-changed';
|
|
214
|
+
change: 'task-created' | 'task-updated' | 'task-deleted' | 'swimlane-updated' | 'backlog-changed' | 'command-terminals-changed';
|
|
203
215
|
ids: string[];
|
|
204
216
|
}
|
|
205
217
|
export interface BoardEvent {
|
|
@@ -208,9 +220,15 @@ export interface BoardEvent {
|
|
|
208
220
|
taskId?: string;
|
|
209
221
|
payload: BoardEventPayload;
|
|
210
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* "Re-read the diff." A task's diff names its `taskId`. A project-folder diff
|
|
225
|
+
* (a `read-diff` request with no `taskId`, protocol 0.20.0, for a Command
|
|
226
|
+
* Terminal's Changes tab) names its `projectId` instead. Exactly one is set.
|
|
227
|
+
*/
|
|
211
228
|
export interface DiffEvent {
|
|
212
229
|
kind: 'diff';
|
|
213
|
-
taskId
|
|
230
|
+
taskId?: string;
|
|
231
|
+
projectId?: string;
|
|
214
232
|
payload: null;
|
|
215
233
|
}
|
|
216
234
|
export type BridgeEvent = TranscriptEvent | ActivityEvent | TerminalEvent | TerminalResizeEvent | BoardEvent | DiffEvent;
|
|
@@ -466,6 +466,35 @@ export interface BoardTaskWire {
|
|
|
466
466
|
*/
|
|
467
467
|
pausable?: boolean | null;
|
|
468
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* One live Command Terminal of a project (protocol 0.20.0): an agent the
|
|
471
|
+
* desktop runs at the project root with no task, started from the desktop's
|
|
472
|
+
* Ctrl+Shift+P layer or by a phone's `command-terminal` start. It is listed
|
|
473
|
+
* beside a project's tasks in a read-board snapshot (`commandTerminals`), and
|
|
474
|
+
* the Agents feed draws it with a "Command Terminal" pill.
|
|
475
|
+
*
|
|
476
|
+
* - `slot` is the terminal's durable number in its project, 1 to 4. It never
|
|
477
|
+
* renumbers while siblings start and stop.
|
|
478
|
+
* - `name` is what to show: the terminal's own name once it has one, else
|
|
479
|
+
* "Command Terminal N". `named` is true once it has its own name (the
|
|
480
|
+
* desktop derives one from the first prompt), which is when the phone's
|
|
481
|
+
* session header adds its pill.
|
|
482
|
+
* - `activity` is the raw state and reason, the same shape `read-stream`
|
|
483
|
+
* reports, so the phone formats the line ("Running Bash", "Awaiting
|
|
484
|
+
* permission") with the formatter it already uses for a task's agent. Both
|
|
485
|
+
* are null until the desktop's activity engine has classified the session.
|
|
486
|
+
*/
|
|
487
|
+
export interface CommandTerminalWire {
|
|
488
|
+
sessionId: string;
|
|
489
|
+
projectId: string;
|
|
490
|
+
slot: number;
|
|
491
|
+
name: string;
|
|
492
|
+
named: boolean;
|
|
493
|
+
activity: {
|
|
494
|
+
state: ActivityStateWire | null;
|
|
495
|
+
reason: ActivityReasonWire | null;
|
|
496
|
+
};
|
|
497
|
+
}
|
|
469
498
|
/** Phone-needed subset of the desktop's BacklogTask row. */
|
|
470
499
|
export interface BacklogItemWire {
|
|
471
500
|
id: string;
|
|
@@ -535,6 +564,12 @@ export declare function parseSessionEventWire(payload: JsonValue): SessionEventW
|
|
|
535
564
|
export declare function parseBoardColumnWire(value: JsonValue): BoardColumnWire;
|
|
536
565
|
/** Narrows one board-task row. Throws on a malformed required field. */
|
|
537
566
|
export declare function parseBoardTaskWire(value: JsonValue): BoardTaskWire;
|
|
567
|
+
/**
|
|
568
|
+
* Narrows one Command Terminal row. Throws on a malformed required field. An
|
|
569
|
+
* unrecognized activity state or reason reads as null rather than throwing, so
|
|
570
|
+
* a reason kind a newer desktop adds costs the line, not the whole snapshot.
|
|
571
|
+
*/
|
|
572
|
+
export declare function parseCommandTerminalWire(value: JsonValue): CommandTerminalWire;
|
|
538
573
|
/** Narrows one backlog-item row. Throws on a malformed required field. */
|
|
539
574
|
export declare function parseBacklogItemWire(value: JsonValue): BacklogItemWire;
|
|
540
575
|
/** Narrows a read-diff file-list payload. Throws on a malformed required field. */
|
package/dist/index.cjs
CHANGED
|
@@ -20,10 +20,13 @@ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: tr
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AGENT_UNAVAILABLE_ERROR_CODE: () => AGENT_UNAVAILABLE_ERROR_CODE,
|
|
23
24
|
BOARD_COLUMN_ROLES: () => BOARD_COLUMN_ROLES,
|
|
24
25
|
BOARD_TOOL_READ_NAMES: () => BOARD_TOOL_READ_NAMES,
|
|
25
26
|
BOARD_TOOL_WRITE_NAMES: () => BOARD_TOOL_WRITE_NAMES,
|
|
26
27
|
CAPABILITY_VERBS: () => CAPABILITY_VERBS,
|
|
28
|
+
COMMAND_TERMINAL_LIMIT_ERROR_CODE: () => COMMAND_TERMINAL_LIMIT_ERROR_CODE,
|
|
29
|
+
COMMAND_TERMINAL_START_FAILED_ERROR_CODE: () => COMMAND_TERMINAL_START_FAILED_ERROR_CODE,
|
|
27
30
|
COMPRESSION_THRESHOLD: () => COMPRESSION_THRESHOLD,
|
|
28
31
|
CipherState: () => CipherState,
|
|
29
32
|
ED25519_KEY_LENGTH: () => ED25519_KEY_LENGTH,
|
|
@@ -34,13 +37,18 @@ __export(index_exports, {
|
|
|
34
37
|
MAX_DECODED_LENGTH: () => MAX_DECODED_LENGTH,
|
|
35
38
|
MAX_FRAME_LENGTH: () => MAX_FRAME_LENGTH,
|
|
36
39
|
MAX_RELAY_ADDRESS_LENGTH: () => MAX_RELAY_ADDRESS_LENGTH,
|
|
40
|
+
NOT_COMMAND_TERMINAL_ERROR_CODE: () => NOT_COMMAND_TERMINAL_ERROR_CODE,
|
|
37
41
|
PAIRING_URI_SCHEME: () => PAIRING_URI_SCHEME,
|
|
38
42
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
39
43
|
PUSH_CATEGORIES: () => PUSH_CATEGORIES,
|
|
40
44
|
PUSH_ENVELOPE_MAX_AGE_MS: () => PUSH_ENVELOPE_MAX_AGE_MS,
|
|
41
45
|
PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS: () => PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
|
|
46
|
+
PUSH_SESSION_KINDS: () => PUSH_SESSION_KINDS,
|
|
47
|
+
RESPONSE_TOO_LARGE_ERROR_CODE: () => RESPONSE_TOO_LARGE_ERROR_CODE,
|
|
42
48
|
SecretstreamState: () => SecretstreamState,
|
|
43
49
|
SessionFrameKind: () => SessionFrameKind,
|
|
50
|
+
UNKNOWN_PROJECT_ERROR_CODE: () => UNKNOWN_PROJECT_ERROR_CODE,
|
|
51
|
+
UNKNOWN_SESSION_ERROR_CODE: () => UNKNOWN_SESSION_ERROR_CODE,
|
|
44
52
|
UNSUPPORTED_VERB_ERROR_CODE: () => UNSUPPORTED_VERB_ERROR_CODE,
|
|
45
53
|
UnsupportedVerbError: () => UnsupportedVerbError,
|
|
46
54
|
X25519_KEY_LENGTH: () => X25519_KEY_LENGTH,
|
|
@@ -58,6 +66,7 @@ __export(index_exports, {
|
|
|
58
66
|
deriveSessionSlotId: () => deriveSessionSlotId,
|
|
59
67
|
deriveShortAuthenticationString: () => deriveShortAuthenticationString,
|
|
60
68
|
encodeMessage: () => encodeMessage,
|
|
69
|
+
encodeMessageFailure: () => encodeMessageFailure,
|
|
61
70
|
encodePairingQrPayload: () => encodePairingQrPayload,
|
|
62
71
|
encodeProtocolVersion: () => encodeProtocolVersion,
|
|
63
72
|
encodeRosterEntryForSigning: () => encodeRosterEntryForSigning,
|
|
@@ -75,6 +84,7 @@ __export(index_exports, {
|
|
|
75
84
|
isDoneRole: () => isDoneRole,
|
|
76
85
|
isJsonValue: () => isJsonValue,
|
|
77
86
|
isPushCategory: () => isPushCategory,
|
|
87
|
+
isPushSessionKind: () => isPushSessionKind,
|
|
78
88
|
isReadStreamSessionStatusWire: () => isReadStreamSessionStatusWire,
|
|
79
89
|
isRecord: () => isRecord,
|
|
80
90
|
isRosterEntryExpired: () => isRosterEntryExpired,
|
|
@@ -88,6 +98,8 @@ __export(index_exports, {
|
|
|
88
98
|
parseBoardColumnWire: () => parseBoardColumnWire,
|
|
89
99
|
parseBoardTaskWire: () => parseBoardTaskWire,
|
|
90
100
|
parseCapabilityRequestPayload: () => parseCapabilityRequestPayload,
|
|
101
|
+
parseCommandTerminalStartResponsePayload: () => parseCommandTerminalStartResponsePayload,
|
|
102
|
+
parseCommandTerminalWire: () => parseCommandTerminalWire,
|
|
91
103
|
parseDiffFileContentWire: () => parseDiffFileContentWire,
|
|
92
104
|
parseDiffFileListWire: () => parseDiffFileListWire,
|
|
93
105
|
parsePauseSessionResponsePayload: () => parsePauseSessionResponsePayload,
|
|
@@ -4973,7 +4985,8 @@ var CAPABILITY_VERBS = [
|
|
|
4973
4985
|
"board-tool-write",
|
|
4974
4986
|
"register-push",
|
|
4975
4987
|
"start-session",
|
|
4976
|
-
"pause-session"
|
|
4988
|
+
"pause-session",
|
|
4989
|
+
"command-terminal"
|
|
4977
4990
|
];
|
|
4978
4991
|
function isCapabilityVerb(value) {
|
|
4979
4992
|
return CAPABILITY_VERBS.includes(value);
|
|
@@ -5010,6 +5023,13 @@ var FRAME_FORMAT_DEFLATE = 1;
|
|
|
5010
5023
|
var JSON_OPEN_BRACE = 123;
|
|
5011
5024
|
var DEFLATE_HEADER_LENGTH = 5;
|
|
5012
5025
|
var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
|
|
5026
|
+
var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
|
|
5027
|
+
var UNKNOWN_PROJECT_ERROR_CODE = "unknown-project";
|
|
5028
|
+
var UNKNOWN_SESSION_ERROR_CODE = "unknown-session";
|
|
5029
|
+
var COMMAND_TERMINAL_LIMIT_ERROR_CODE = "command-terminal-limit";
|
|
5030
|
+
var NOT_COMMAND_TERMINAL_ERROR_CODE = "not-command-terminal";
|
|
5031
|
+
var AGENT_UNAVAILABLE_ERROR_CODE = "agent-unavailable";
|
|
5032
|
+
var COMMAND_TERMINAL_START_FAILED_ERROR_CODE = "command-terminal-start-failed";
|
|
5013
5033
|
var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
|
|
5014
5034
|
var UnsupportedVerbError = class extends Error {
|
|
5015
5035
|
requestId;
|
|
@@ -5046,6 +5066,15 @@ function encodeMessage(message) {
|
|
|
5046
5066
|
}
|
|
5047
5067
|
return frame;
|
|
5048
5068
|
}
|
|
5069
|
+
function encodeMessageFailure(message) {
|
|
5070
|
+
try {
|
|
5071
|
+
if (new TextEncoder().encode(JSON.stringify(message)).length <= MAX_FRAME_LENGTH) return null;
|
|
5072
|
+
encodeMessage(message);
|
|
5073
|
+
return null;
|
|
5074
|
+
} catch (error) {
|
|
5075
|
+
return error instanceof Error ? error.message : String(error);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5049
5078
|
function decodeMessage(bytes) {
|
|
5050
5079
|
if (bytes.length > MAX_FRAME_LENGTH) {
|
|
5051
5080
|
throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
|
|
@@ -5088,7 +5117,11 @@ function validateEvent(event) {
|
|
|
5088
5117
|
return event;
|
|
5089
5118
|
}
|
|
5090
5119
|
case "diff": {
|
|
5091
|
-
if (typeof event.taskId !== "string") throw new Error('"diff" event
|
|
5120
|
+
if (event.taskId !== void 0 && typeof event.taskId !== "string") throw new Error('"diff" event has a non-string "taskId"');
|
|
5121
|
+
if (event.projectId !== void 0 && typeof event.projectId !== "string") throw new Error('"diff" event has a non-string "projectId"');
|
|
5122
|
+
if (event.taskId === void 0 === (event.projectId === void 0)) {
|
|
5123
|
+
throw new Error('"diff" event must carry exactly one of "taskId" and "projectId"');
|
|
5124
|
+
}
|
|
5092
5125
|
return event;
|
|
5093
5126
|
}
|
|
5094
5127
|
default:
|
|
@@ -5201,6 +5234,10 @@ function isPushCategory(value) {
|
|
|
5201
5234
|
}
|
|
5202
5235
|
var PUSH_ENVELOPE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
5203
5236
|
var PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS = 5 * 60 * 1e3;
|
|
5237
|
+
var PUSH_SESSION_KINDS = ["command-terminal"];
|
|
5238
|
+
function isPushSessionKind(value) {
|
|
5239
|
+
return typeof value === "string" && PUSH_SESSION_KINDS.includes(value);
|
|
5240
|
+
}
|
|
5204
5241
|
function requireEnvelopeString(record, field) {
|
|
5205
5242
|
const value = record[field];
|
|
5206
5243
|
if (typeof value !== "string") throw new Error(`push envelope plaintext is missing a string "${field}"`);
|
|
@@ -5219,7 +5256,10 @@ function parsePushEnvelopePlaintext(value) {
|
|
|
5219
5256
|
sessionId: requireEnvelopeString(value, "sessionId"),
|
|
5220
5257
|
taskTitle: requireEnvelopeString(value, "taskTitle"),
|
|
5221
5258
|
detail: requireEnvelopeString(value, "detail"),
|
|
5222
|
-
sentAt: value.sentAt
|
|
5259
|
+
sentAt: value.sentAt,
|
|
5260
|
+
// An unknown kind (from a newer desktop) is dropped, not rejected, so the
|
|
5261
|
+
// notification still opens; the tap then routes as a task's would.
|
|
5262
|
+
...isPushSessionKind(value.sessionKind) ? { sessionKind: value.sessionKind } : {}
|
|
5223
5263
|
};
|
|
5224
5264
|
}
|
|
5225
5265
|
function sealPushEnvelope(pushKey, recipientStaticPublicKey, plaintext) {
|
|
@@ -5472,6 +5512,21 @@ function parseBoardTaskWire(value) {
|
|
|
5472
5512
|
pausable: nullableBoolean(value, "pausable")
|
|
5473
5513
|
};
|
|
5474
5514
|
}
|
|
5515
|
+
function parseCommandTerminalWire(value) {
|
|
5516
|
+
if (!isRecord(value)) throw new Error("command terminal must be an object");
|
|
5517
|
+
const activity = isRecord(value.activity) ? value.activity : {};
|
|
5518
|
+
return {
|
|
5519
|
+
sessionId: requireString(value, "sessionId", "command terminal"),
|
|
5520
|
+
projectId: requireString(value, "projectId", "command terminal"),
|
|
5521
|
+
slot: requireNumber(value, "slot", "command terminal"),
|
|
5522
|
+
name: requireString(value, "name", "command terminal"),
|
|
5523
|
+
named: requireBoolean(value, "named", "command terminal"),
|
|
5524
|
+
activity: {
|
|
5525
|
+
state: isActivityStateWire(activity.state) ? activity.state : null,
|
|
5526
|
+
reason: isActivityReasonWire(activity.reason) ? activity.reason : null
|
|
5527
|
+
}
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5475
5530
|
function parseBacklogItemWire(value) {
|
|
5476
5531
|
if (!isRecord(value)) throw new Error("backlog item must be an object");
|
|
5477
5532
|
return {
|
|
@@ -5664,6 +5719,12 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5664
5719
|
if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
|
|
5665
5720
|
throw new Error('read-board response has an invalid "view"');
|
|
5666
5721
|
}
|
|
5722
|
+
if (payload.commandTerminals !== void 0 && !Array.isArray(payload.commandTerminals)) {
|
|
5723
|
+
throw new Error('read-board response has a non-array "commandTerminals"');
|
|
5724
|
+
}
|
|
5725
|
+
if (payload.commandTerminalsOffered !== void 0 && typeof payload.commandTerminalsOffered !== "boolean") {
|
|
5726
|
+
throw new Error('read-board response has a non-boolean "commandTerminalsOffered"');
|
|
5727
|
+
}
|
|
5667
5728
|
return {
|
|
5668
5729
|
projectId: payload.projectId,
|
|
5669
5730
|
columns: payload.columns.map((column) => parseBoardColumnWire(column)),
|
|
@@ -5672,7 +5733,9 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5672
5733
|
...payload.projectColor !== void 0 ? { projectColor: parseAccentColor(payload.projectColor, "read-board snapshot") } : {},
|
|
5673
5734
|
...payload.showTicketNumbers !== void 0 ? { showTicketNumbers: payload.showTicketNumbers } : {},
|
|
5674
5735
|
...payload.view !== void 0 ? { view: payload.view } : {},
|
|
5675
|
-
...payload.taskCountsByColumnId !== void 0 ? { taskCountsByColumnId: parseTaskCountsByColumnId(payload.taskCountsByColumnId) } : {}
|
|
5736
|
+
...payload.taskCountsByColumnId !== void 0 ? { taskCountsByColumnId: parseTaskCountsByColumnId(payload.taskCountsByColumnId) } : {},
|
|
5737
|
+
...payload.commandTerminals !== void 0 ? { commandTerminals: payload.commandTerminals.map((terminal) => parseCommandTerminalWire(terminal)) } : {},
|
|
5738
|
+
...payload.commandTerminalsOffered !== void 0 ? { commandTerminalsOffered: payload.commandTerminalsOffered } : {}
|
|
5676
5739
|
};
|
|
5677
5740
|
}
|
|
5678
5741
|
function parseTaskCountsByColumnId(value) {
|
|
@@ -5726,7 +5789,7 @@ function parseReadDiffResponsePayload(payload) {
|
|
|
5726
5789
|
}
|
|
5727
5790
|
function parseReadDiffRequestPayload(payload) {
|
|
5728
5791
|
if (!isRecord(payload)) throw new Error("read-diff payload must be an object");
|
|
5729
|
-
if (typeof payload.taskId !== "string") throw new Error('read-diff payload
|
|
5792
|
+
if (payload.taskId !== void 0 && typeof payload.taskId !== "string") throw new Error('read-diff payload has a non-string "taskId"');
|
|
5730
5793
|
if (typeof payload.projectId !== "string") throw new Error('read-diff payload missing "projectId"');
|
|
5731
5794
|
if (payload.filePath !== void 0 && typeof payload.filePath !== "string") {
|
|
5732
5795
|
throw new Error('read-diff payload has a non-string "filePath"');
|
|
@@ -5738,7 +5801,7 @@ function parseReadDiffRequestPayload(payload) {
|
|
|
5738
5801
|
throw new Error('read-diff payload has an invalid "action"');
|
|
5739
5802
|
}
|
|
5740
5803
|
return {
|
|
5741
|
-
taskId: payload.taskId,
|
|
5804
|
+
...payload.taskId !== void 0 ? { taskId: payload.taskId } : {},
|
|
5742
5805
|
projectId: payload.projectId,
|
|
5743
5806
|
filePath: payload.filePath,
|
|
5744
5807
|
scope: payload.scope,
|
|
@@ -5790,6 +5853,28 @@ function parsePauseSessionRequestPayload(payload) {
|
|
|
5790
5853
|
projectId: payload.projectId
|
|
5791
5854
|
};
|
|
5792
5855
|
}
|
|
5856
|
+
function parseCommandTerminalRequestPayload(payload) {
|
|
5857
|
+
if (!isRecord(payload)) throw new Error("command-terminal payload must be an object");
|
|
5858
|
+
if (payload.action === "start") {
|
|
5859
|
+
if (typeof payload.projectId !== "string") throw new Error('command-terminal start payload missing "projectId"');
|
|
5860
|
+
return { action: "start", projectId: payload.projectId };
|
|
5861
|
+
}
|
|
5862
|
+
if (payload.action === "stop") {
|
|
5863
|
+
if (typeof payload.sessionId !== "string") throw new Error('command-terminal stop payload missing "sessionId"');
|
|
5864
|
+
return { action: "stop", sessionId: payload.sessionId };
|
|
5865
|
+
}
|
|
5866
|
+
throw new Error('command-terminal payload has an invalid "action"');
|
|
5867
|
+
}
|
|
5868
|
+
function parseCommandTerminalStartResponsePayload(payload) {
|
|
5869
|
+
if (!isRecord(payload)) throw new Error("command-terminal start response must be an object");
|
|
5870
|
+
if (payload.ok !== true) throw new Error('command-terminal start response missing "ok"');
|
|
5871
|
+
if (typeof payload.sessionId !== "string") throw new Error('command-terminal start response missing "sessionId"');
|
|
5872
|
+
if (typeof payload.slot !== "number" || !Number.isInteger(payload.slot) || payload.slot < 1) {
|
|
5873
|
+
throw new Error('command-terminal start response has an invalid "slot"');
|
|
5874
|
+
}
|
|
5875
|
+
if (typeof payload.name !== "string") throw new Error('command-terminal start response missing "name"');
|
|
5876
|
+
return { ok: true, sessionId: payload.sessionId, slot: payload.slot, name: payload.name };
|
|
5877
|
+
}
|
|
5793
5878
|
function parsePauseSessionResponsePayload(payload) {
|
|
5794
5879
|
if (!isRecord(payload)) throw new Error("pause-session response must be an object");
|
|
5795
5880
|
if (typeof payload.ok !== "boolean") throw new Error('pause-session response missing "ok"');
|
|
@@ -5894,6 +5979,8 @@ function parseCapabilityRequestPayload(verb, payload) {
|
|
|
5894
5979
|
return parseStartSessionRequestPayload(payload);
|
|
5895
5980
|
case "pause-session":
|
|
5896
5981
|
return parsePauseSessionRequestPayload(payload);
|
|
5982
|
+
case "command-terminal":
|
|
5983
|
+
return parseCommandTerminalRequestPayload(payload);
|
|
5897
5984
|
default: {
|
|
5898
5985
|
const exhaustiveCheck = verb;
|
|
5899
5986
|
throw new Error(`Unknown capability verb: ${String(exhaustiveCheck)}`);
|
|
@@ -6124,7 +6211,14 @@ function isBoardToolWriteName(value) {
|
|
|
6124
6211
|
}
|
|
6125
6212
|
|
|
6126
6213
|
// src/events/event.ts
|
|
6127
|
-
var BOARD_CHANGES = [
|
|
6214
|
+
var BOARD_CHANGES = [
|
|
6215
|
+
"task-created",
|
|
6216
|
+
"task-updated",
|
|
6217
|
+
"task-deleted",
|
|
6218
|
+
"swimlane-updated",
|
|
6219
|
+
"backlog-changed",
|
|
6220
|
+
"command-terminals-changed"
|
|
6221
|
+
];
|
|
6128
6222
|
function parseActivityEventPayload(payload) {
|
|
6129
6223
|
if (!isRecord(payload)) throw new Error("activity payload must be an object");
|
|
6130
6224
|
switch (payload.type) {
|
|
@@ -6219,18 +6313,27 @@ function isBridgeEvent(value) {
|
|
|
6219
6313
|
if (typeof change !== "string" || !BOARD_CHANGES.includes(change)) return false;
|
|
6220
6314
|
return Array.isArray(value.payload.ids) && value.payload.ids.every((id) => typeof id === "string");
|
|
6221
6315
|
}
|
|
6222
|
-
case "diff":
|
|
6223
|
-
|
|
6316
|
+
case "diff": {
|
|
6317
|
+
if (value.payload !== null) return false;
|
|
6318
|
+
const hasTaskId = typeof value.taskId === "string";
|
|
6319
|
+
const hasProjectId = typeof value.projectId === "string";
|
|
6320
|
+
if (value.taskId !== void 0 && !hasTaskId) return false;
|
|
6321
|
+
if (value.projectId !== void 0 && !hasProjectId) return false;
|
|
6322
|
+
return hasTaskId !== hasProjectId;
|
|
6323
|
+
}
|
|
6224
6324
|
default:
|
|
6225
6325
|
return false;
|
|
6226
6326
|
}
|
|
6227
6327
|
}
|
|
6228
6328
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6229
6329
|
0 && (module.exports = {
|
|
6330
|
+
AGENT_UNAVAILABLE_ERROR_CODE,
|
|
6230
6331
|
BOARD_COLUMN_ROLES,
|
|
6231
6332
|
BOARD_TOOL_READ_NAMES,
|
|
6232
6333
|
BOARD_TOOL_WRITE_NAMES,
|
|
6233
6334
|
CAPABILITY_VERBS,
|
|
6335
|
+
COMMAND_TERMINAL_LIMIT_ERROR_CODE,
|
|
6336
|
+
COMMAND_TERMINAL_START_FAILED_ERROR_CODE,
|
|
6234
6337
|
COMPRESSION_THRESHOLD,
|
|
6235
6338
|
CipherState,
|
|
6236
6339
|
ED25519_KEY_LENGTH,
|
|
@@ -6241,13 +6344,18 @@ function isBridgeEvent(value) {
|
|
|
6241
6344
|
MAX_DECODED_LENGTH,
|
|
6242
6345
|
MAX_FRAME_LENGTH,
|
|
6243
6346
|
MAX_RELAY_ADDRESS_LENGTH,
|
|
6347
|
+
NOT_COMMAND_TERMINAL_ERROR_CODE,
|
|
6244
6348
|
PAIRING_URI_SCHEME,
|
|
6245
6349
|
PROTOCOL_VERSION,
|
|
6246
6350
|
PUSH_CATEGORIES,
|
|
6247
6351
|
PUSH_ENVELOPE_MAX_AGE_MS,
|
|
6248
6352
|
PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
|
|
6353
|
+
PUSH_SESSION_KINDS,
|
|
6354
|
+
RESPONSE_TOO_LARGE_ERROR_CODE,
|
|
6249
6355
|
SecretstreamState,
|
|
6250
6356
|
SessionFrameKind,
|
|
6357
|
+
UNKNOWN_PROJECT_ERROR_CODE,
|
|
6358
|
+
UNKNOWN_SESSION_ERROR_CODE,
|
|
6251
6359
|
UNSUPPORTED_VERB_ERROR_CODE,
|
|
6252
6360
|
UnsupportedVerbError,
|
|
6253
6361
|
X25519_KEY_LENGTH,
|
|
@@ -6265,6 +6373,7 @@ function isBridgeEvent(value) {
|
|
|
6265
6373
|
deriveSessionSlotId,
|
|
6266
6374
|
deriveShortAuthenticationString,
|
|
6267
6375
|
encodeMessage,
|
|
6376
|
+
encodeMessageFailure,
|
|
6268
6377
|
encodePairingQrPayload,
|
|
6269
6378
|
encodeProtocolVersion,
|
|
6270
6379
|
encodeRosterEntryForSigning,
|
|
@@ -6282,6 +6391,7 @@ function isBridgeEvent(value) {
|
|
|
6282
6391
|
isDoneRole,
|
|
6283
6392
|
isJsonValue,
|
|
6284
6393
|
isPushCategory,
|
|
6394
|
+
isPushSessionKind,
|
|
6285
6395
|
isReadStreamSessionStatusWire,
|
|
6286
6396
|
isRecord,
|
|
6287
6397
|
isRosterEntryExpired,
|
|
@@ -6295,6 +6405,8 @@ function isBridgeEvent(value) {
|
|
|
6295
6405
|
parseBoardColumnWire,
|
|
6296
6406
|
parseBoardTaskWire,
|
|
6297
6407
|
parseCapabilityRequestPayload,
|
|
6408
|
+
parseCommandTerminalStartResponsePayload,
|
|
6409
|
+
parseCommandTerminalWire,
|
|
6298
6410
|
parseDiffFileContentWire,
|
|
6299
6411
|
parseDiffFileListWire,
|
|
6300
6412
|
parsePauseSessionResponsePayload,
|
package/dist/index.d.ts
CHANGED
|
@@ -9,11 +9,11 @@ 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, UNKNOWN_PROJECT_ERROR_CODE, UNKNOWN_SESSION_ERROR_CODE, COMMAND_TERMINAL_LIMIT_ERROR_CODE, NOT_COMMAND_TERMINAL_ERROR_CODE, AGENT_UNAVAILABLE_ERROR_CODE, COMMAND_TERMINAL_START_FAILED_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, 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
|
-
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';
|
|
15
|
+
export { parseCapabilityRequestPayload, parseReadStreamResponsePayload, parseReadBoardResponsePayload, parseReadDiffResponsePayload, parseTranscriptWindowResponsePayload, parseRegisterPushRequestPayload, parseStartSessionResponsePayload, parsePauseSessionResponsePayload, parseCommandTerminalStartResponsePayload, 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, type CommandTerminalRequestPayload, type CommandTerminalStartResponsePayload, type CommandTerminalStopResponsePayload, type CommandTerminalResponsePayload, } from './wire/payloads';
|
|
16
|
+
export { PUSH_CATEGORIES, isPushCategory, PUSH_SESSION_KINDS, isPushSessionKind, parsePushEnvelopePlaintext, sealPushEnvelope, openPushEnvelope, PUSH_ENVELOPE_MAX_AGE_MS, PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS, type PushCategory, type PushSessionKind, 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';
|
|
19
19
|
export { MAX_RELAY_ADDRESS_LENGTH, isSecureRelayAddress } from './pairing/relay-address';
|
|
@@ -22,5 +22,5 @@ export { formatKeyFingerprint } from './roster/fingerprint';
|
|
|
22
22
|
export { CAPABILITY_VERBS, isCapabilityVerb, capabilitySetFromArray, capabilitySetToArray, type CapabilityVerb, type CapabilitySet, } from './capabilities/verbs';
|
|
23
23
|
export { BOARD_TOOL_READ_NAMES, BOARD_TOOL_WRITE_NAMES, isBoardToolReadName, isBoardToolWriteName, type BoardToolReadName, type BoardToolWriteName, type BoardToolName, } from './capabilities/board-tools';
|
|
24
24
|
export { isBridgeEvent, parseActivityEventPayload, type BridgeEvent, type TranscriptEvent, type ActivityEvent, type ActivityEventPayload, type TerminalEvent, type TerminalResizeEvent, type BoardEvent, type BoardEventPayload, type DiffEvent, } from './events/event';
|
|
25
|
-
export { parseTranscriptEntriesWire, parseTranscriptEventPayload, type TranscriptEventPayload, type TranscriptUpsertWire, isActivityStateWire, isActivityReasonWire, parseSessionUsageWire, parseSessionSummaryWire, parseSessionEventWire, parseTerminalDimensionsWire, parseBoardColumnWire, parseBoardTaskWire, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, isTodoRole, isDoneRole, BOARD_COLUMN_ROLES, type TranscriptBlockWire, type TranscriptEntryWire, type TranscriptTurnUsageWire, type TranscriptSystemSubtypeWire, type ActivityStateWire, type ActivityReasonWire, type SessionUsageWire, type SessionSummaryWire, type SessionEventWire, type TerminalDimensionsWire, type BoardColumnWire, type BoardColumnRoleWire, type BoardTaskWire, type BacklogItemWire, type DiffFileStatusWire, type DiffFileWire, type DiffFileListWire, type DiffFileContentWire, } from './events/payloads';
|
|
25
|
+
export { parseTranscriptEntriesWire, parseTranscriptEventPayload, type TranscriptEventPayload, type TranscriptUpsertWire, isActivityStateWire, isActivityReasonWire, parseSessionUsageWire, parseSessionSummaryWire, parseSessionEventWire, parseTerminalDimensionsWire, parseBoardColumnWire, parseBoardTaskWire, parseCommandTerminalWire, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, isTodoRole, isDoneRole, BOARD_COLUMN_ROLES, type TranscriptBlockWire, type TranscriptEntryWire, type TranscriptTurnUsageWire, type TranscriptSystemSubtypeWire, type ActivityStateWire, type ActivityReasonWire, type SessionUsageWire, type SessionSummaryWire, type SessionEventWire, type TerminalDimensionsWire, type BoardColumnWire, type BoardColumnRoleWire, type BoardTaskWire, type CommandTerminalWire, type BacklogItemWire, type DiffFileStatusWire, type DiffFileWire, type DiffFileListWire, type DiffFileContentWire, } from './events/payloads';
|
|
26
26
|
export type { Transport, TransportState, Unsubscribe } from './transport/transport';
|
package/dist/index.mjs
CHANGED
|
@@ -4846,7 +4846,8 @@ var CAPABILITY_VERBS = [
|
|
|
4846
4846
|
"board-tool-write",
|
|
4847
4847
|
"register-push",
|
|
4848
4848
|
"start-session",
|
|
4849
|
-
"pause-session"
|
|
4849
|
+
"pause-session",
|
|
4850
|
+
"command-terminal"
|
|
4850
4851
|
];
|
|
4851
4852
|
function isCapabilityVerb(value) {
|
|
4852
4853
|
return CAPABILITY_VERBS.includes(value);
|
|
@@ -4883,6 +4884,13 @@ var FRAME_FORMAT_DEFLATE = 1;
|
|
|
4883
4884
|
var JSON_OPEN_BRACE = 123;
|
|
4884
4885
|
var DEFLATE_HEADER_LENGTH = 5;
|
|
4885
4886
|
var UNSUPPORTED_VERB_ERROR_CODE = "unsupported-verb";
|
|
4887
|
+
var RESPONSE_TOO_LARGE_ERROR_CODE = "response-too-large";
|
|
4888
|
+
var UNKNOWN_PROJECT_ERROR_CODE = "unknown-project";
|
|
4889
|
+
var UNKNOWN_SESSION_ERROR_CODE = "unknown-session";
|
|
4890
|
+
var COMMAND_TERMINAL_LIMIT_ERROR_CODE = "command-terminal-limit";
|
|
4891
|
+
var NOT_COMMAND_TERMINAL_ERROR_CODE = "not-command-terminal";
|
|
4892
|
+
var AGENT_UNAVAILABLE_ERROR_CODE = "agent-unavailable";
|
|
4893
|
+
var COMMAND_TERMINAL_START_FAILED_ERROR_CODE = "command-terminal-start-failed";
|
|
4886
4894
|
var UNSUPPORTED_VERB_ERROR_NAME = "UnsupportedVerbError";
|
|
4887
4895
|
var UnsupportedVerbError = class extends Error {
|
|
4888
4896
|
requestId;
|
|
@@ -4919,6 +4927,15 @@ function encodeMessage(message) {
|
|
|
4919
4927
|
}
|
|
4920
4928
|
return frame;
|
|
4921
4929
|
}
|
|
4930
|
+
function encodeMessageFailure(message) {
|
|
4931
|
+
try {
|
|
4932
|
+
if (new TextEncoder().encode(JSON.stringify(message)).length <= MAX_FRAME_LENGTH) return null;
|
|
4933
|
+
encodeMessage(message);
|
|
4934
|
+
return null;
|
|
4935
|
+
} catch (error) {
|
|
4936
|
+
return error instanceof Error ? error.message : String(error);
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4922
4939
|
function decodeMessage(bytes) {
|
|
4923
4940
|
if (bytes.length > MAX_FRAME_LENGTH) {
|
|
4924
4941
|
throw new Error(`Bridge message frame exceeds ${MAX_FRAME_LENGTH} bytes`);
|
|
@@ -4961,7 +4978,11 @@ function validateEvent(event) {
|
|
|
4961
4978
|
return event;
|
|
4962
4979
|
}
|
|
4963
4980
|
case "diff": {
|
|
4964
|
-
if (typeof event.taskId !== "string") throw new Error('"diff" event
|
|
4981
|
+
if (event.taskId !== void 0 && typeof event.taskId !== "string") throw new Error('"diff" event has a non-string "taskId"');
|
|
4982
|
+
if (event.projectId !== void 0 && typeof event.projectId !== "string") throw new Error('"diff" event has a non-string "projectId"');
|
|
4983
|
+
if (event.taskId === void 0 === (event.projectId === void 0)) {
|
|
4984
|
+
throw new Error('"diff" event must carry exactly one of "taskId" and "projectId"');
|
|
4985
|
+
}
|
|
4965
4986
|
return event;
|
|
4966
4987
|
}
|
|
4967
4988
|
default:
|
|
@@ -5074,6 +5095,10 @@ function isPushCategory(value) {
|
|
|
5074
5095
|
}
|
|
5075
5096
|
var PUSH_ENVELOPE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
5076
5097
|
var PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS = 5 * 60 * 1e3;
|
|
5098
|
+
var PUSH_SESSION_KINDS = ["command-terminal"];
|
|
5099
|
+
function isPushSessionKind(value) {
|
|
5100
|
+
return typeof value === "string" && PUSH_SESSION_KINDS.includes(value);
|
|
5101
|
+
}
|
|
5077
5102
|
function requireEnvelopeString(record, field) {
|
|
5078
5103
|
const value = record[field];
|
|
5079
5104
|
if (typeof value !== "string") throw new Error(`push envelope plaintext is missing a string "${field}"`);
|
|
@@ -5092,7 +5117,10 @@ function parsePushEnvelopePlaintext(value) {
|
|
|
5092
5117
|
sessionId: requireEnvelopeString(value, "sessionId"),
|
|
5093
5118
|
taskTitle: requireEnvelopeString(value, "taskTitle"),
|
|
5094
5119
|
detail: requireEnvelopeString(value, "detail"),
|
|
5095
|
-
sentAt: value.sentAt
|
|
5120
|
+
sentAt: value.sentAt,
|
|
5121
|
+
// An unknown kind (from a newer desktop) is dropped, not rejected, so the
|
|
5122
|
+
// notification still opens; the tap then routes as a task's would.
|
|
5123
|
+
...isPushSessionKind(value.sessionKind) ? { sessionKind: value.sessionKind } : {}
|
|
5096
5124
|
};
|
|
5097
5125
|
}
|
|
5098
5126
|
function sealPushEnvelope(pushKey, recipientStaticPublicKey, plaintext) {
|
|
@@ -5345,6 +5373,21 @@ function parseBoardTaskWire(value) {
|
|
|
5345
5373
|
pausable: nullableBoolean(value, "pausable")
|
|
5346
5374
|
};
|
|
5347
5375
|
}
|
|
5376
|
+
function parseCommandTerminalWire(value) {
|
|
5377
|
+
if (!isRecord(value)) throw new Error("command terminal must be an object");
|
|
5378
|
+
const activity = isRecord(value.activity) ? value.activity : {};
|
|
5379
|
+
return {
|
|
5380
|
+
sessionId: requireString(value, "sessionId", "command terminal"),
|
|
5381
|
+
projectId: requireString(value, "projectId", "command terminal"),
|
|
5382
|
+
slot: requireNumber(value, "slot", "command terminal"),
|
|
5383
|
+
name: requireString(value, "name", "command terminal"),
|
|
5384
|
+
named: requireBoolean(value, "named", "command terminal"),
|
|
5385
|
+
activity: {
|
|
5386
|
+
state: isActivityStateWire(activity.state) ? activity.state : null,
|
|
5387
|
+
reason: isActivityReasonWire(activity.reason) ? activity.reason : null
|
|
5388
|
+
}
|
|
5389
|
+
};
|
|
5390
|
+
}
|
|
5348
5391
|
function parseBacklogItemWire(value) {
|
|
5349
5392
|
if (!isRecord(value)) throw new Error("backlog item must be an object");
|
|
5350
5393
|
return {
|
|
@@ -5537,6 +5580,12 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5537
5580
|
if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
|
|
5538
5581
|
throw new Error('read-board response has an invalid "view"');
|
|
5539
5582
|
}
|
|
5583
|
+
if (payload.commandTerminals !== void 0 && !Array.isArray(payload.commandTerminals)) {
|
|
5584
|
+
throw new Error('read-board response has a non-array "commandTerminals"');
|
|
5585
|
+
}
|
|
5586
|
+
if (payload.commandTerminalsOffered !== void 0 && typeof payload.commandTerminalsOffered !== "boolean") {
|
|
5587
|
+
throw new Error('read-board response has a non-boolean "commandTerminalsOffered"');
|
|
5588
|
+
}
|
|
5540
5589
|
return {
|
|
5541
5590
|
projectId: payload.projectId,
|
|
5542
5591
|
columns: payload.columns.map((column) => parseBoardColumnWire(column)),
|
|
@@ -5545,7 +5594,9 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5545
5594
|
...payload.projectColor !== void 0 ? { projectColor: parseAccentColor(payload.projectColor, "read-board snapshot") } : {},
|
|
5546
5595
|
...payload.showTicketNumbers !== void 0 ? { showTicketNumbers: payload.showTicketNumbers } : {},
|
|
5547
5596
|
...payload.view !== void 0 ? { view: payload.view } : {},
|
|
5548
|
-
...payload.taskCountsByColumnId !== void 0 ? { taskCountsByColumnId: parseTaskCountsByColumnId(payload.taskCountsByColumnId) } : {}
|
|
5597
|
+
...payload.taskCountsByColumnId !== void 0 ? { taskCountsByColumnId: parseTaskCountsByColumnId(payload.taskCountsByColumnId) } : {},
|
|
5598
|
+
...payload.commandTerminals !== void 0 ? { commandTerminals: payload.commandTerminals.map((terminal) => parseCommandTerminalWire(terminal)) } : {},
|
|
5599
|
+
...payload.commandTerminalsOffered !== void 0 ? { commandTerminalsOffered: payload.commandTerminalsOffered } : {}
|
|
5549
5600
|
};
|
|
5550
5601
|
}
|
|
5551
5602
|
function parseTaskCountsByColumnId(value) {
|
|
@@ -5599,7 +5650,7 @@ function parseReadDiffResponsePayload(payload) {
|
|
|
5599
5650
|
}
|
|
5600
5651
|
function parseReadDiffRequestPayload(payload) {
|
|
5601
5652
|
if (!isRecord(payload)) throw new Error("read-diff payload must be an object");
|
|
5602
|
-
if (typeof payload.taskId !== "string") throw new Error('read-diff payload
|
|
5653
|
+
if (payload.taskId !== void 0 && typeof payload.taskId !== "string") throw new Error('read-diff payload has a non-string "taskId"');
|
|
5603
5654
|
if (typeof payload.projectId !== "string") throw new Error('read-diff payload missing "projectId"');
|
|
5604
5655
|
if (payload.filePath !== void 0 && typeof payload.filePath !== "string") {
|
|
5605
5656
|
throw new Error('read-diff payload has a non-string "filePath"');
|
|
@@ -5611,7 +5662,7 @@ function parseReadDiffRequestPayload(payload) {
|
|
|
5611
5662
|
throw new Error('read-diff payload has an invalid "action"');
|
|
5612
5663
|
}
|
|
5613
5664
|
return {
|
|
5614
|
-
taskId: payload.taskId,
|
|
5665
|
+
...payload.taskId !== void 0 ? { taskId: payload.taskId } : {},
|
|
5615
5666
|
projectId: payload.projectId,
|
|
5616
5667
|
filePath: payload.filePath,
|
|
5617
5668
|
scope: payload.scope,
|
|
@@ -5663,6 +5714,28 @@ function parsePauseSessionRequestPayload(payload) {
|
|
|
5663
5714
|
projectId: payload.projectId
|
|
5664
5715
|
};
|
|
5665
5716
|
}
|
|
5717
|
+
function parseCommandTerminalRequestPayload(payload) {
|
|
5718
|
+
if (!isRecord(payload)) throw new Error("command-terminal payload must be an object");
|
|
5719
|
+
if (payload.action === "start") {
|
|
5720
|
+
if (typeof payload.projectId !== "string") throw new Error('command-terminal start payload missing "projectId"');
|
|
5721
|
+
return { action: "start", projectId: payload.projectId };
|
|
5722
|
+
}
|
|
5723
|
+
if (payload.action === "stop") {
|
|
5724
|
+
if (typeof payload.sessionId !== "string") throw new Error('command-terminal stop payload missing "sessionId"');
|
|
5725
|
+
return { action: "stop", sessionId: payload.sessionId };
|
|
5726
|
+
}
|
|
5727
|
+
throw new Error('command-terminal payload has an invalid "action"');
|
|
5728
|
+
}
|
|
5729
|
+
function parseCommandTerminalStartResponsePayload(payload) {
|
|
5730
|
+
if (!isRecord(payload)) throw new Error("command-terminal start response must be an object");
|
|
5731
|
+
if (payload.ok !== true) throw new Error('command-terminal start response missing "ok"');
|
|
5732
|
+
if (typeof payload.sessionId !== "string") throw new Error('command-terminal start response missing "sessionId"');
|
|
5733
|
+
if (typeof payload.slot !== "number" || !Number.isInteger(payload.slot) || payload.slot < 1) {
|
|
5734
|
+
throw new Error('command-terminal start response has an invalid "slot"');
|
|
5735
|
+
}
|
|
5736
|
+
if (typeof payload.name !== "string") throw new Error('command-terminal start response missing "name"');
|
|
5737
|
+
return { ok: true, sessionId: payload.sessionId, slot: payload.slot, name: payload.name };
|
|
5738
|
+
}
|
|
5666
5739
|
function parsePauseSessionResponsePayload(payload) {
|
|
5667
5740
|
if (!isRecord(payload)) throw new Error("pause-session response must be an object");
|
|
5668
5741
|
if (typeof payload.ok !== "boolean") throw new Error('pause-session response missing "ok"');
|
|
@@ -5767,6 +5840,8 @@ function parseCapabilityRequestPayload(verb, payload) {
|
|
|
5767
5840
|
return parseStartSessionRequestPayload(payload);
|
|
5768
5841
|
case "pause-session":
|
|
5769
5842
|
return parsePauseSessionRequestPayload(payload);
|
|
5843
|
+
case "command-terminal":
|
|
5844
|
+
return parseCommandTerminalRequestPayload(payload);
|
|
5770
5845
|
default: {
|
|
5771
5846
|
const exhaustiveCheck = verb;
|
|
5772
5847
|
throw new Error(`Unknown capability verb: ${String(exhaustiveCheck)}`);
|
|
@@ -5997,7 +6072,14 @@ function isBoardToolWriteName(value) {
|
|
|
5997
6072
|
}
|
|
5998
6073
|
|
|
5999
6074
|
// src/events/event.ts
|
|
6000
|
-
var BOARD_CHANGES = [
|
|
6075
|
+
var BOARD_CHANGES = [
|
|
6076
|
+
"task-created",
|
|
6077
|
+
"task-updated",
|
|
6078
|
+
"task-deleted",
|
|
6079
|
+
"swimlane-updated",
|
|
6080
|
+
"backlog-changed",
|
|
6081
|
+
"command-terminals-changed"
|
|
6082
|
+
];
|
|
6001
6083
|
function parseActivityEventPayload(payload) {
|
|
6002
6084
|
if (!isRecord(payload)) throw new Error("activity payload must be an object");
|
|
6003
6085
|
switch (payload.type) {
|
|
@@ -6092,17 +6174,26 @@ function isBridgeEvent(value) {
|
|
|
6092
6174
|
if (typeof change !== "string" || !BOARD_CHANGES.includes(change)) return false;
|
|
6093
6175
|
return Array.isArray(value.payload.ids) && value.payload.ids.every((id) => typeof id === "string");
|
|
6094
6176
|
}
|
|
6095
|
-
case "diff":
|
|
6096
|
-
|
|
6177
|
+
case "diff": {
|
|
6178
|
+
if (value.payload !== null) return false;
|
|
6179
|
+
const hasTaskId = typeof value.taskId === "string";
|
|
6180
|
+
const hasProjectId = typeof value.projectId === "string";
|
|
6181
|
+
if (value.taskId !== void 0 && !hasTaskId) return false;
|
|
6182
|
+
if (value.projectId !== void 0 && !hasProjectId) return false;
|
|
6183
|
+
return hasTaskId !== hasProjectId;
|
|
6184
|
+
}
|
|
6097
6185
|
default:
|
|
6098
6186
|
return false;
|
|
6099
6187
|
}
|
|
6100
6188
|
}
|
|
6101
6189
|
export {
|
|
6190
|
+
AGENT_UNAVAILABLE_ERROR_CODE,
|
|
6102
6191
|
BOARD_COLUMN_ROLES,
|
|
6103
6192
|
BOARD_TOOL_READ_NAMES,
|
|
6104
6193
|
BOARD_TOOL_WRITE_NAMES,
|
|
6105
6194
|
CAPABILITY_VERBS,
|
|
6195
|
+
COMMAND_TERMINAL_LIMIT_ERROR_CODE,
|
|
6196
|
+
COMMAND_TERMINAL_START_FAILED_ERROR_CODE,
|
|
6106
6197
|
COMPRESSION_THRESHOLD,
|
|
6107
6198
|
CipherState,
|
|
6108
6199
|
ED25519_KEY_LENGTH,
|
|
@@ -6113,13 +6204,18 @@ export {
|
|
|
6113
6204
|
MAX_DECODED_LENGTH,
|
|
6114
6205
|
MAX_FRAME_LENGTH,
|
|
6115
6206
|
MAX_RELAY_ADDRESS_LENGTH,
|
|
6207
|
+
NOT_COMMAND_TERMINAL_ERROR_CODE,
|
|
6116
6208
|
PAIRING_URI_SCHEME,
|
|
6117
6209
|
PROTOCOL_VERSION,
|
|
6118
6210
|
PUSH_CATEGORIES,
|
|
6119
6211
|
PUSH_ENVELOPE_MAX_AGE_MS,
|
|
6120
6212
|
PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS,
|
|
6213
|
+
PUSH_SESSION_KINDS,
|
|
6214
|
+
RESPONSE_TOO_LARGE_ERROR_CODE,
|
|
6121
6215
|
SecretstreamState,
|
|
6122
6216
|
SessionFrameKind,
|
|
6217
|
+
UNKNOWN_PROJECT_ERROR_CODE,
|
|
6218
|
+
UNKNOWN_SESSION_ERROR_CODE,
|
|
6123
6219
|
UNSUPPORTED_VERB_ERROR_CODE,
|
|
6124
6220
|
UnsupportedVerbError,
|
|
6125
6221
|
X25519_KEY_LENGTH,
|
|
@@ -6137,6 +6233,7 @@ export {
|
|
|
6137
6233
|
deriveSessionSlotId,
|
|
6138
6234
|
deriveShortAuthenticationString,
|
|
6139
6235
|
encodeMessage,
|
|
6236
|
+
encodeMessageFailure,
|
|
6140
6237
|
encodePairingQrPayload,
|
|
6141
6238
|
encodeProtocolVersion,
|
|
6142
6239
|
encodeRosterEntryForSigning,
|
|
@@ -6154,6 +6251,7 @@ export {
|
|
|
6154
6251
|
isDoneRole,
|
|
6155
6252
|
isJsonValue,
|
|
6156
6253
|
isPushCategory,
|
|
6254
|
+
isPushSessionKind,
|
|
6157
6255
|
isReadStreamSessionStatusWire,
|
|
6158
6256
|
isRecord,
|
|
6159
6257
|
isRosterEntryExpired,
|
|
@@ -6167,6 +6265,8 @@ export {
|
|
|
6167
6265
|
parseBoardColumnWire,
|
|
6168
6266
|
parseBoardTaskWire,
|
|
6169
6267
|
parseCapabilityRequestPayload,
|
|
6268
|
+
parseCommandTerminalStartResponsePayload,
|
|
6269
|
+
parseCommandTerminalWire,
|
|
6170
6270
|
parseDiffFileContentWire,
|
|
6171
6271
|
parseDiffFileListWire,
|
|
6172
6272
|
parsePauseSessionResponsePayload,
|
package/dist/wire/framing.d.ts
CHANGED
|
@@ -6,6 +6,20 @@ 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;
|
|
11
|
+
/** The `code` on a refusal that named a project this desktop does not have. */
|
|
12
|
+
export declare const UNKNOWN_PROJECT_ERROR_CODE: CapabilityErrorCode;
|
|
13
|
+
/** The `code` on a refusal that named a session id that is not live. */
|
|
14
|
+
export declare const UNKNOWN_SESSION_ERROR_CODE: CapabilityErrorCode;
|
|
15
|
+
/** The `code` on a `command-terminal` start refused at the per-project cap. */
|
|
16
|
+
export declare const COMMAND_TERMINAL_LIMIT_ERROR_CODE: CapabilityErrorCode;
|
|
17
|
+
/** The `code` on a `command-terminal` stop that named a session which is not a Command Terminal. */
|
|
18
|
+
export declare const NOT_COMMAND_TERMINAL_ERROR_CODE: CapabilityErrorCode;
|
|
19
|
+
/** The `code` on a start refused because the project's agent CLI is not installed. */
|
|
20
|
+
export declare const AGENT_UNAVAILABLE_ERROR_CODE: CapabilityErrorCode;
|
|
21
|
+
/** The `code` on a `command-terminal` start that failed for any other reason. */
|
|
22
|
+
export declare const COMMAND_TERMINAL_START_FAILED_ERROR_CODE: CapabilityErrorCode;
|
|
9
23
|
/**
|
|
10
24
|
* Thrown by `decodeMessage` for a capability-request whose envelope is
|
|
11
25
|
* well-formed (a string `requestId`, a string `verb`, a JSON `payload`) but
|
|
@@ -34,4 +48,17 @@ export declare class UnsupportedVerbError extends Error {
|
|
|
34
48
|
*/
|
|
35
49
|
export declare function isUnsupportedVerbError(error: unknown): error is UnsupportedVerbError;
|
|
36
50
|
export declare function encodeMessage(message: BridgeMessage): Uint8Array;
|
|
51
|
+
/**
|
|
52
|
+
* Why `encodeMessage` would throw for `message`, or null when it would not,
|
|
53
|
+
* without keeping the frame. A sender with side effects asks this BEFORE
|
|
54
|
+
* committing them, so it can send something smaller instead of committing to
|
|
55
|
+
* a message it cannot send (the desktop re-takes a shorter terminal seed).
|
|
56
|
+
*
|
|
57
|
+
* Exact, not an estimate. Raw UTF-8 JSON at or under MAX_FRAME_LENGTH always
|
|
58
|
+
* fits, because `encodeMessage` only switches to deflate when deflate is
|
|
59
|
+
* smaller, so that common case costs one stringify. A larger message runs the
|
|
60
|
+
* real encode, deflate included, and a sender that then sends it pays that
|
|
61
|
+
* encode a second time. Measured terminal seeds are far under the threshold.
|
|
62
|
+
*/
|
|
63
|
+
export declare function encodeMessageFailure(message: BridgeMessage): string | null;
|
|
37
64
|
export declare function decodeMessage(bytes: Uint8Array): BridgeMessage;
|
package/dist/wire/messages.d.ts
CHANGED
|
@@ -32,13 +32,37 @@ 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.
|
|
48
|
+
* - `unknown-project`: the request named a project id this desktop does not
|
|
49
|
+
* have (removed, or never registered).
|
|
50
|
+
* - `unknown-session`: the request named a session id that is not live. For a
|
|
51
|
+
* `command-terminal` stop it means the terminal already ended, so a phone
|
|
52
|
+
* treats it as stopped.
|
|
53
|
+
* - `command-terminal-limit`: the project already runs the most Command
|
|
54
|
+
* Terminals a project may have at once (four).
|
|
55
|
+
* - `not-command-terminal`: a `command-terminal` stop named a session that is
|
|
56
|
+
* not a Command Terminal (a task's agent). Nothing was stopped.
|
|
57
|
+
* - `agent-unavailable`: the project's agent CLI could not be found on the
|
|
58
|
+
* desktop, so nothing could be started.
|
|
59
|
+
* - `command-terminal-start-failed`: a `command-terminal` start failed for any
|
|
60
|
+
* other reason. `error` carries the desktop's text for logs.
|
|
61
|
+
*
|
|
62
|
+
* A peer must treat a code it does not know as a generic failure and show
|
|
63
|
+
* `error` or a plain line of its own: a newer desktop may add codes.
|
|
40
64
|
*/
|
|
41
|
-
export type CapabilityErrorCode = 'unsupported-verb';
|
|
65
|
+
export type CapabilityErrorCode = 'unsupported-verb' | 'response-too-large' | 'unknown-project' | 'unknown-session' | 'command-terminal-limit' | 'not-command-terminal' | 'agent-unavailable' | 'command-terminal-start-failed';
|
|
42
66
|
export interface CapabilityResponseMessage {
|
|
43
67
|
type: 'capability-response';
|
|
44
68
|
requestId: string;
|
package/dist/wire/payloads.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import type { CapabilityVerb } from '../capabilities/verbs';
|
|
17
17
|
import type { JsonValue } from './messages';
|
|
18
18
|
import { type PushCategory } from '../crypto/push-envelope';
|
|
19
|
-
import { type ActivityReasonWire, type ActivityStateWire, type BacklogItemWire, type BoardColumnWire, type BoardTaskWire, type DiffFileContentWire, type DiffFileListWire, type SessionSummaryWire, type SessionUsageWire, type TerminalDimensionsWire, type TranscriptEntryWire } from '../events/payloads';
|
|
19
|
+
import { type ActivityReasonWire, type ActivityStateWire, type BacklogItemWire, type BoardColumnWire, type BoardTaskWire, type CommandTerminalWire, type DiffFileContentWire, type DiffFileListWire, type SessionSummaryWire, type SessionUsageWire, type TerminalDimensionsWire, type TranscriptEntryWire } from '../events/payloads';
|
|
20
20
|
export interface ReadStreamRequestPayload {
|
|
21
21
|
sessionId: string;
|
|
22
22
|
/**
|
|
@@ -251,6 +251,20 @@ export interface ReadBoardSnapshotResponsePayload {
|
|
|
251
251
|
* card to a column needs the real length.
|
|
252
252
|
*/
|
|
253
253
|
taskCountsByColumnId?: Record<string, number>;
|
|
254
|
+
/**
|
|
255
|
+
* The project's live Command Terminals, ordered by slot (protocol 0.20.0).
|
|
256
|
+
* Carried by every snapshot view, because the Agents feed lists them beside
|
|
257
|
+
* every project's task agents. Absent from an older desktop, which has no way
|
|
258
|
+
* to report them. A `command-terminals-changed` board event says to re-read it.
|
|
259
|
+
*/
|
|
260
|
+
commandTerminals?: CommandTerminalWire[];
|
|
261
|
+
/**
|
|
262
|
+
* True when this desktop serves the `command-terminal` verb AND the device
|
|
263
|
+
* holds it (protocol 0.20.0). The phone keeps no grant list, so it shows its
|
|
264
|
+
* Terminals button on this flag, the way `BoardTaskWire.pausable` gates Pause.
|
|
265
|
+
* Absent from an older desktop: treat that as false.
|
|
266
|
+
*/
|
|
267
|
+
commandTerminalsOffered?: boolean;
|
|
254
268
|
}
|
|
255
269
|
/**
|
|
256
270
|
* Returned for `action: 'archived'` - one page of a project's completed tasks
|
|
@@ -283,7 +297,14 @@ export declare function parseReadBoardResponsePayload(payload: JsonValue): ReadB
|
|
|
283
297
|
/** Mirrors DiffService's GitDiffScope in the desktop app; kept as a literal union here rather than an import, since the protocol package does not depend on the app. */
|
|
284
298
|
export type ReadDiffScope = 'working' | 'staged' | 'branch';
|
|
285
299
|
export interface ReadDiffRequestPayload {
|
|
286
|
-
|
|
300
|
+
/**
|
|
301
|
+
* The task whose worktree (or, for a task with none, whose base) to diff.
|
|
302
|
+
* Optional from protocol 0.20.0: with no `taskId` the desktop diffs the
|
|
303
|
+
* PROJECT FOLDER against the project's default base branch, exactly as a
|
|
304
|
+
* Command Terminal window's Changes panel does, and its live pings arrive
|
|
305
|
+
* as a `DiffEvent` carrying `projectId` instead of `taskId`.
|
|
306
|
+
*/
|
|
307
|
+
taskId?: string;
|
|
287
308
|
projectId: string;
|
|
288
309
|
filePath?: string;
|
|
289
310
|
scope?: ReadDiffScope;
|
|
@@ -363,6 +384,50 @@ export interface PauseSessionRequestPayload {
|
|
|
363
384
|
export interface PauseSessionResponsePayload {
|
|
364
385
|
ok: boolean;
|
|
365
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Start or stop a Command Terminal (protocol 0.20.0). See `command-terminal` in
|
|
389
|
+
* capabilities/verbs.ts for what one is.
|
|
390
|
+
*
|
|
391
|
+
* `start` names only the project. The desktop picks the slot, runs the
|
|
392
|
+
* project's default agent with its default model and effort, and starts on the
|
|
393
|
+
* project's default branch the same way its own New terminal does; there is no
|
|
394
|
+
* branch, model or effort parameter, and the parser below drops any such key.
|
|
395
|
+
* `stop` names the terminal's session and is refused for any other session.
|
|
396
|
+
*/
|
|
397
|
+
export type CommandTerminalRequestPayload = {
|
|
398
|
+
action: 'start';
|
|
399
|
+
projectId: string;
|
|
400
|
+
} | {
|
|
401
|
+
action: 'stop';
|
|
402
|
+
sessionId: string;
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* A `start` is answered once the desktop has the session, before the agent has
|
|
406
|
+
* drawn anything, so the phone opens the terminal's screen at once and its
|
|
407
|
+
* stream shows the agent arriving. `slot` is the terminal's number in its
|
|
408
|
+
* project (1 to 4) and `name` its label, "Command Terminal N" until the
|
|
409
|
+
* desktop names it from its first prompt.
|
|
410
|
+
*
|
|
411
|
+
* Refusals carry a `code` on the capability response: `command-terminal-limit`,
|
|
412
|
+
* `unknown-project`, `agent-unavailable` or `command-terminal-start-failed`.
|
|
413
|
+
*/
|
|
414
|
+
export interface CommandTerminalStartResponsePayload {
|
|
415
|
+
ok: true;
|
|
416
|
+
sessionId: string;
|
|
417
|
+
slot: number;
|
|
418
|
+
name: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* A `stop` is answered once the desktop has ended the terminal. Refusals carry
|
|
422
|
+
* `unknown-session` (it already ended, so treat it as stopped) or
|
|
423
|
+
* `not-command-terminal`.
|
|
424
|
+
*/
|
|
425
|
+
export interface CommandTerminalStopResponsePayload {
|
|
426
|
+
ok: true;
|
|
427
|
+
}
|
|
428
|
+
export type CommandTerminalResponsePayload = CommandTerminalStartResponsePayload | CommandTerminalStopResponsePayload;
|
|
429
|
+
/** Phone-side guard: narrows a decoded `command-terminal` start response before the phone opens the terminal. */
|
|
430
|
+
export declare function parseCommandTerminalStartResponsePayload(payload: JsonValue): CommandTerminalStartResponsePayload;
|
|
366
431
|
/** Phone-side guard: narrows a decoded pause-session response before the phone acts on it. */
|
|
367
432
|
export declare function parsePauseSessionResponsePayload(payload: JsonValue): PauseSessionResponsePayload;
|
|
368
433
|
export interface AnswerPermissionPromptRequestPayload {
|
|
@@ -452,6 +517,7 @@ export interface CapabilityRequestPayloadMap {
|
|
|
452
517
|
'register-push': RegisterPushRequestPayload;
|
|
453
518
|
'start-session': StartSessionRequestPayload;
|
|
454
519
|
'pause-session': PauseSessionRequestPayload;
|
|
520
|
+
'command-terminal': CommandTerminalRequestPayload;
|
|
455
521
|
}
|
|
456
522
|
export interface CapabilityResponsePayloadMap {
|
|
457
523
|
'read-stream': ReadStreamResponsePayload | TranscriptWindowResponsePayload;
|
|
@@ -466,6 +532,7 @@ export interface CapabilityResponsePayloadMap {
|
|
|
466
532
|
'register-push': RegisterPushResponsePayload;
|
|
467
533
|
'start-session': StartSessionResponsePayload;
|
|
468
534
|
'pause-session': PauseSessionResponsePayload;
|
|
535
|
+
'command-terminal': CommandTerminalResponsePayload;
|
|
469
536
|
}
|
|
470
537
|
/**
|
|
471
538
|
* 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.20.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",
|