@kangentic/protocol 0.1.0 → 0.2.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/LICENSE +659 -4
- package/dist/capabilities/board-tools.d.ts +22 -0
- package/dist/capabilities/verbs.d.ts +1 -1
- package/dist/crypto/slot.d.ts +1 -0
- package/dist/events/event.d.ts +76 -16
- package/dist/events/payloads.d.ts +220 -0
- package/dist/index.cjs +548 -23
- package/dist/index.d.ts +6 -1
- package/dist/index.mjs +525 -23
- package/dist/wire/json-value.d.ts +9 -0
- package/dist/wire/payloads.d.ts +139 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,8 @@ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: tr
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
BOARD_TOOL_READ_NAMES: () => BOARD_TOOL_READ_NAMES,
|
|
24
|
+
BOARD_TOOL_WRITE_NAMES: () => BOARD_TOOL_WRITE_NAMES,
|
|
23
25
|
CAPABILITY_VERBS: () => CAPABILITY_VERBS,
|
|
24
26
|
CipherState: () => CipherState,
|
|
25
27
|
ED25519_KEY_LENGTH: () => ED25519_KEY_LENGTH,
|
|
@@ -43,6 +45,7 @@ __export(index_exports, {
|
|
|
43
45
|
decodeMessage: () => decodeMessage,
|
|
44
46
|
decodePairingQrPayload: () => decodePairingQrPayload,
|
|
45
47
|
deriveSecretstreamPair: () => deriveSecretstreamPair,
|
|
48
|
+
deriveSessionSlotId: () => deriveSessionSlotId,
|
|
46
49
|
deriveShortAuthenticationString: () => deriveShortAuthenticationString,
|
|
47
50
|
encodeMessage: () => encodeMessage,
|
|
48
51
|
encodePairingQrPayload: () => encodePairingQrPayload,
|
|
@@ -52,8 +55,28 @@ __export(index_exports, {
|
|
|
52
55
|
generateEd25519KeyPair: () => generateEd25519KeyPair,
|
|
53
56
|
generateX25519KeyPair: () => generateX25519KeyPair,
|
|
54
57
|
hexToBytes: () => hexToBytes,
|
|
58
|
+
isActivityReasonWire: () => isActivityReasonWire,
|
|
59
|
+
isActivityStateWire: () => isActivityStateWire,
|
|
60
|
+
isBoardToolReadName: () => isBoardToolReadName,
|
|
61
|
+
isBoardToolWriteName: () => isBoardToolWriteName,
|
|
62
|
+
isBridgeEvent: () => isBridgeEvent,
|
|
55
63
|
isCapabilityVerb: () => isCapabilityVerb,
|
|
64
|
+
isJsonValue: () => isJsonValue,
|
|
65
|
+
isRecord: () => isRecord,
|
|
56
66
|
isRosterEntryExpired: () => isRosterEntryExpired,
|
|
67
|
+
parseActivityEventPayload: () => parseActivityEventPayload,
|
|
68
|
+
parseBacklogItemWire: () => parseBacklogItemWire,
|
|
69
|
+
parseBoardColumnWire: () => parseBoardColumnWire,
|
|
70
|
+
parseBoardTaskWire: () => parseBoardTaskWire,
|
|
71
|
+
parseCapabilityRequestPayload: () => parseCapabilityRequestPayload,
|
|
72
|
+
parseDiffFileContentWire: () => parseDiffFileContentWire,
|
|
73
|
+
parseDiffFileListWire: () => parseDiffFileListWire,
|
|
74
|
+
parseReadBoardResponsePayload: () => parseReadBoardResponsePayload,
|
|
75
|
+
parseReadDiffResponsePayload: () => parseReadDiffResponsePayload,
|
|
76
|
+
parseReadStreamResponsePayload: () => parseReadStreamResponsePayload,
|
|
77
|
+
parseSessionEventWire: () => parseSessionEventWire,
|
|
78
|
+
parseSessionUsageWire: () => parseSessionUsageWire,
|
|
79
|
+
parseTranscriptEntriesWire: () => parseTranscriptEntriesWire,
|
|
57
80
|
randomBytes: () => randomBytes4,
|
|
58
81
|
rosterDeviceCapabilitySet: () => rosterDeviceCapabilitySet,
|
|
59
82
|
signRosterEntry: () => signRosterEntry,
|
|
@@ -4236,6 +4259,14 @@ function deriveShortAuthenticationString(handshakeHash) {
|
|
|
4236
4259
|
return { digits, emoji };
|
|
4237
4260
|
}
|
|
4238
4261
|
|
|
4262
|
+
// src/crypto/slot.ts
|
|
4263
|
+
var SESSION_SLOT_LABEL = "kangentic-session-slot-v1";
|
|
4264
|
+
var SESSION_SLOT_LENGTH = 16;
|
|
4265
|
+
function deriveSessionSlotId(desktopStaticPublicKey, phoneStaticPublicKey) {
|
|
4266
|
+
const material = concatBytes4(desktopStaticPublicKey, phoneStaticPublicKey);
|
|
4267
|
+
return bytesToHex(deriveLabeledKey(material, SESSION_SLOT_LABEL, SESSION_SLOT_LENGTH));
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4239
4270
|
// src/capabilities/verbs.ts
|
|
4240
4271
|
var CAPABILITY_VERBS = [
|
|
4241
4272
|
"read-stream",
|
|
@@ -4243,7 +4274,10 @@ var CAPABILITY_VERBS = [
|
|
|
4243
4274
|
"read-diff",
|
|
4244
4275
|
"send-user-message",
|
|
4245
4276
|
"move-task",
|
|
4246
|
-
"answer-permission-prompt"
|
|
4277
|
+
"answer-permission-prompt",
|
|
4278
|
+
"interactive-terminal",
|
|
4279
|
+
"board-tool-read",
|
|
4280
|
+
"board-tool-write"
|
|
4247
4281
|
];
|
|
4248
4282
|
function isCapabilityVerb(value) {
|
|
4249
4283
|
return CAPABILITY_VERBS.includes(value);
|
|
@@ -4259,6 +4293,19 @@ function capabilitySetToArray(capabilities) {
|
|
|
4259
4293
|
return Array.from(capabilities);
|
|
4260
4294
|
}
|
|
4261
4295
|
|
|
4296
|
+
// src/wire/json-value.ts
|
|
4297
|
+
function isJsonValue(value) {
|
|
4298
|
+
if (value === null) return true;
|
|
4299
|
+
const valueType = typeof value;
|
|
4300
|
+
if (valueType === "boolean" || valueType === "number" || valueType === "string") return true;
|
|
4301
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
4302
|
+
if (valueType === "object") return Object.values(value).every(isJsonValue);
|
|
4303
|
+
return false;
|
|
4304
|
+
}
|
|
4305
|
+
function isRecord(value) {
|
|
4306
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4262
4309
|
// src/wire/framing.ts
|
|
4263
4310
|
var MAX_FRAME_LENGTH = 1024 * 1024;
|
|
4264
4311
|
function encodeMessage(message) {
|
|
@@ -4275,16 +4322,29 @@ function decodeMessage(bytes) {
|
|
|
4275
4322
|
const parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
4276
4323
|
return validateBridgeMessage(parsed);
|
|
4277
4324
|
}
|
|
4278
|
-
function
|
|
4279
|
-
if (
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4325
|
+
function validateEvent(event) {
|
|
4326
|
+
if (typeof event.kind !== "string") throw new Error('event message missing a valid "kind"');
|
|
4327
|
+
if (!isJsonValue(event.payload)) throw new Error("event payload is not JSON-serializable");
|
|
4328
|
+
switch (event.kind) {
|
|
4329
|
+
case "transcript":
|
|
4330
|
+
case "activity":
|
|
4331
|
+
case "terminal": {
|
|
4332
|
+
if (typeof event.sessionId !== "string") throw new Error(`"${event.kind}" event is missing "sessionId"`);
|
|
4333
|
+
if (typeof event.taskId !== "string") throw new Error(`"${event.kind}" event is missing "taskId"`);
|
|
4334
|
+
return event;
|
|
4335
|
+
}
|
|
4336
|
+
case "board": {
|
|
4337
|
+
if (typeof event.projectId !== "string") throw new Error('"board" event is missing "projectId"');
|
|
4338
|
+
if (event.taskId !== void 0 && typeof event.taskId !== "string") throw new Error('"board" event has a non-string "taskId"');
|
|
4339
|
+
return event;
|
|
4340
|
+
}
|
|
4341
|
+
case "diff": {
|
|
4342
|
+
if (typeof event.taskId !== "string") throw new Error('"diff" event is missing "taskId"');
|
|
4343
|
+
return event;
|
|
4344
|
+
}
|
|
4345
|
+
default:
|
|
4346
|
+
throw new Error(`event message has an unknown event kind: ${event.kind}`);
|
|
4347
|
+
}
|
|
4288
4348
|
}
|
|
4289
4349
|
function validateBridgeMessage(value) {
|
|
4290
4350
|
if (!isRecord(value) || typeof value.type !== "string") {
|
|
@@ -4313,18 +4373,8 @@ function validateBridgeMessage(value) {
|
|
|
4313
4373
|
};
|
|
4314
4374
|
}
|
|
4315
4375
|
case "event": {
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
throw new Error('event message missing a valid "event"');
|
|
4319
|
-
}
|
|
4320
|
-
if (typeof event.taskId !== "string") throw new Error('event is missing "taskId"');
|
|
4321
|
-
if (!isJsonValue(event.payload)) throw new Error("event payload is not JSON-serializable");
|
|
4322
|
-
if (event.kind === "transcript" || event.kind === "activity") {
|
|
4323
|
-
if (typeof event.sessionId !== "string") throw new Error(`"${event.kind}" event is missing "sessionId"`);
|
|
4324
|
-
} else if (event.kind !== "board") {
|
|
4325
|
-
throw new Error(`event message has an unknown event kind: ${event.kind}`);
|
|
4326
|
-
}
|
|
4327
|
-
return { type: "event", event };
|
|
4376
|
+
if (!isRecord(value.event)) throw new Error('event message missing a valid "event"');
|
|
4377
|
+
return { type: "event", event: validateEvent(value.event) };
|
|
4328
4378
|
}
|
|
4329
4379
|
default:
|
|
4330
4380
|
throw new Error(`Unknown bridge message type: ${value.type}`);
|
|
@@ -4351,6 +4401,360 @@ function unwrapSessionFrame(frame) {
|
|
|
4351
4401
|
return { kind, payload: frame.subarray(1) };
|
|
4352
4402
|
}
|
|
4353
4403
|
|
|
4404
|
+
// src/events/payloads.ts
|
|
4405
|
+
var DIFF_FILE_STATUSES = ["A", "M", "D", "R", "C", "U"];
|
|
4406
|
+
var TRANSCRIPT_SYSTEM_SUBTYPES = ["compaction", "command", "command_output", "session_boundary"];
|
|
4407
|
+
function requireString(record, field, context) {
|
|
4408
|
+
const value = record[field];
|
|
4409
|
+
if (typeof value !== "string") throw new Error(`${context} is missing a string "${field}"`);
|
|
4410
|
+
return value;
|
|
4411
|
+
}
|
|
4412
|
+
function requireNumber(record, field, context) {
|
|
4413
|
+
const value = record[field];
|
|
4414
|
+
if (typeof value !== "number") throw new Error(`${context} is missing a number "${field}"`);
|
|
4415
|
+
return value;
|
|
4416
|
+
}
|
|
4417
|
+
function requireBoolean(record, field, context) {
|
|
4418
|
+
const value = record[field];
|
|
4419
|
+
if (typeof value !== "boolean") throw new Error(`${context} is missing a boolean "${field}"`);
|
|
4420
|
+
return value;
|
|
4421
|
+
}
|
|
4422
|
+
function isTranscriptBlockWire(value) {
|
|
4423
|
+
if (!isRecord(value)) return false;
|
|
4424
|
+
if (value.type === "text" || value.type === "thinking") return typeof value.text === "string";
|
|
4425
|
+
if (value.type === "tool_use") return typeof value.id === "string" && typeof value.name === "string" && "input" in value;
|
|
4426
|
+
return false;
|
|
4427
|
+
}
|
|
4428
|
+
function isTranscriptEntryWire(value) {
|
|
4429
|
+
if (!isRecord(value)) return false;
|
|
4430
|
+
if (typeof value.uuid !== "string" || typeof value.ts !== "number") return false;
|
|
4431
|
+
switch (value.kind) {
|
|
4432
|
+
case "user":
|
|
4433
|
+
return typeof value.text === "string";
|
|
4434
|
+
case "assistant":
|
|
4435
|
+
return Array.isArray(value.blocks) && value.blocks.every(isTranscriptBlockWire);
|
|
4436
|
+
case "tool_result":
|
|
4437
|
+
return typeof value.toolUseId === "string" && typeof value.content === "string";
|
|
4438
|
+
case "system":
|
|
4439
|
+
return typeof value.subtype === "string" && TRANSCRIPT_SYSTEM_SUBTYPES.includes(value.subtype) && typeof value.text === "string";
|
|
4440
|
+
default:
|
|
4441
|
+
return false;
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
function parseTranscriptEntriesWire(payload) {
|
|
4445
|
+
if (!Array.isArray(payload)) throw new Error("transcript payload must be an array of entries");
|
|
4446
|
+
return payload.map((entry, index) => {
|
|
4447
|
+
if (!isTranscriptEntryWire(entry)) throw new Error(`transcript payload entry ${index} is malformed`);
|
|
4448
|
+
return entry;
|
|
4449
|
+
});
|
|
4450
|
+
}
|
|
4451
|
+
function isActivityStateWire(value) {
|
|
4452
|
+
return value === "thinking" || value === "idle" || value === "permission";
|
|
4453
|
+
}
|
|
4454
|
+
function isActivityReasonWire(value) {
|
|
4455
|
+
if (!isRecord(value)) return false;
|
|
4456
|
+
switch (value.kind) {
|
|
4457
|
+
case "idle":
|
|
4458
|
+
case "permission":
|
|
4459
|
+
case "turn-active":
|
|
4460
|
+
return true;
|
|
4461
|
+
case "tool":
|
|
4462
|
+
return typeof value.pendingCount === "number" && (value.currentTool === null || typeof value.currentTool === "string");
|
|
4463
|
+
case "subagent":
|
|
4464
|
+
return typeof value.depth === "number";
|
|
4465
|
+
case "background-shell":
|
|
4466
|
+
return typeof value.count === "number" && Array.isArray(value.ids) && value.ids.every((id) => typeof id === "string");
|
|
4467
|
+
default:
|
|
4468
|
+
return false;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
function parseSessionUsageWire(payload) {
|
|
4472
|
+
if (!isRecord(payload)) throw new Error("usage payload must be an object");
|
|
4473
|
+
const contextWindow = payload.contextWindow;
|
|
4474
|
+
if (!isRecord(contextWindow)) throw new Error('usage payload is missing "contextWindow"');
|
|
4475
|
+
const cost = payload.cost;
|
|
4476
|
+
if (!isRecord(cost)) throw new Error('usage payload is missing "cost"');
|
|
4477
|
+
const model = payload.model;
|
|
4478
|
+
if (!isRecord(model)) throw new Error('usage payload is missing "model"');
|
|
4479
|
+
return {
|
|
4480
|
+
contextWindow: {
|
|
4481
|
+
usedPercentage: requireNumber(contextWindow, "usedPercentage", "usage contextWindow"),
|
|
4482
|
+
usedTokens: requireNumber(contextWindow, "usedTokens", "usage contextWindow"),
|
|
4483
|
+
cacheTokens: requireNumber(contextWindow, "cacheTokens", "usage contextWindow"),
|
|
4484
|
+
totalInputTokens: requireNumber(contextWindow, "totalInputTokens", "usage contextWindow"),
|
|
4485
|
+
totalOutputTokens: requireNumber(contextWindow, "totalOutputTokens", "usage contextWindow"),
|
|
4486
|
+
contextWindowSize: requireNumber(contextWindow, "contextWindowSize", "usage contextWindow")
|
|
4487
|
+
},
|
|
4488
|
+
cost: {
|
|
4489
|
+
totalCostUsd: requireNumber(cost, "totalCostUsd", "usage cost"),
|
|
4490
|
+
totalDurationMs: requireNumber(cost, "totalDurationMs", "usage cost")
|
|
4491
|
+
},
|
|
4492
|
+
...typeof payload.toolCallCount === "number" ? { toolCallCount: payload.toolCallCount } : {},
|
|
4493
|
+
model: {
|
|
4494
|
+
id: requireString(model, "id", "usage model"),
|
|
4495
|
+
displayName: requireString(model, "displayName", "usage model"),
|
|
4496
|
+
...typeof model.effort === "string" ? { effort: model.effort } : {}
|
|
4497
|
+
}
|
|
4498
|
+
};
|
|
4499
|
+
}
|
|
4500
|
+
function parseSessionEventWire(payload) {
|
|
4501
|
+
if (!isRecord(payload)) throw new Error("session event payload must be an object");
|
|
4502
|
+
return {
|
|
4503
|
+
ts: requireNumber(payload, "ts", "session event"),
|
|
4504
|
+
type: requireString(payload, "type", "session event"),
|
|
4505
|
+
...typeof payload.tool === "string" ? { tool: payload.tool } : {},
|
|
4506
|
+
...typeof payload.toolId === "string" ? { toolId: payload.toolId } : {},
|
|
4507
|
+
...typeof payload.detail === "string" ? { detail: payload.detail } : {}
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
function parseStringArray(value, context) {
|
|
4511
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
|
4512
|
+
throw new Error(`${context} is not a string array`);
|
|
4513
|
+
}
|
|
4514
|
+
return value;
|
|
4515
|
+
}
|
|
4516
|
+
function nullableString(record, field) {
|
|
4517
|
+
const value = record[field];
|
|
4518
|
+
return typeof value === "string" ? value : null;
|
|
4519
|
+
}
|
|
4520
|
+
function nullableNumber(record, field) {
|
|
4521
|
+
const value = record[field];
|
|
4522
|
+
return typeof value === "number" ? value : null;
|
|
4523
|
+
}
|
|
4524
|
+
function parseBoardColumnWire(value) {
|
|
4525
|
+
if (!isRecord(value)) throw new Error("board column must be an object");
|
|
4526
|
+
return {
|
|
4527
|
+
id: requireString(value, "id", "board column"),
|
|
4528
|
+
name: requireString(value, "name", "board column"),
|
|
4529
|
+
description: nullableString(value, "description"),
|
|
4530
|
+
role: nullableString(value, "role"),
|
|
4531
|
+
position: requireNumber(value, "position", "board column"),
|
|
4532
|
+
color: requireString(value, "color", "board column"),
|
|
4533
|
+
icon: nullableString(value, "icon"),
|
|
4534
|
+
is_archived: requireBoolean(value, "is_archived", "board column"),
|
|
4535
|
+
is_ghost: requireBoolean(value, "is_ghost", "board column")
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
function parseBoardTaskWire(value) {
|
|
4539
|
+
if (!isRecord(value)) throw new Error("board task must be an object");
|
|
4540
|
+
return {
|
|
4541
|
+
id: requireString(value, "id", "board task"),
|
|
4542
|
+
display_id: requireNumber(value, "display_id", "board task"),
|
|
4543
|
+
title: requireString(value, "title", "board task"),
|
|
4544
|
+
description: typeof value.description === "string" ? value.description : "",
|
|
4545
|
+
swimlane_id: requireString(value, "swimlane_id", "board task"),
|
|
4546
|
+
position: requireNumber(value, "position", "board task"),
|
|
4547
|
+
agent: nullableString(value, "agent"),
|
|
4548
|
+
session_id: nullableString(value, "session_id"),
|
|
4549
|
+
worktree_path: nullableString(value, "worktree_path"),
|
|
4550
|
+
branch_name: nullableString(value, "branch_name"),
|
|
4551
|
+
pr_number: nullableNumber(value, "pr_number"),
|
|
4552
|
+
pr_url: nullableString(value, "pr_url"),
|
|
4553
|
+
pr_state: nullableString(value, "pr_state"),
|
|
4554
|
+
base_branch: nullableString(value, "base_branch"),
|
|
4555
|
+
labels: Array.isArray(value.labels) ? parseStringArray(value.labels, "board task labels") : [],
|
|
4556
|
+
priority: typeof value.priority === "number" ? value.priority : 0,
|
|
4557
|
+
attachment_count: typeof value.attachment_count === "number" ? value.attachment_count : 0,
|
|
4558
|
+
archived_at: nullableString(value, "archived_at"),
|
|
4559
|
+
created_at: requireString(value, "created_at", "board task"),
|
|
4560
|
+
updated_at: requireString(value, "updated_at", "board task")
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
function parseBacklogItemWire(value) {
|
|
4564
|
+
if (!isRecord(value)) throw new Error("backlog item must be an object");
|
|
4565
|
+
return {
|
|
4566
|
+
id: requireString(value, "id", "backlog item"),
|
|
4567
|
+
title: requireString(value, "title", "backlog item"),
|
|
4568
|
+
description: typeof value.description === "string" ? value.description : "",
|
|
4569
|
+
priority: typeof value.priority === "number" ? value.priority : 0,
|
|
4570
|
+
labels: Array.isArray(value.labels) ? parseStringArray(value.labels, "backlog item labels") : [],
|
|
4571
|
+
position: requireNumber(value, "position", "backlog item"),
|
|
4572
|
+
item_type: nullableString(value, "item_type"),
|
|
4573
|
+
external_url: nullableString(value, "external_url"),
|
|
4574
|
+
attachment_count: typeof value.attachment_count === "number" ? value.attachment_count : 0,
|
|
4575
|
+
created_at: requireString(value, "created_at", "backlog item"),
|
|
4576
|
+
updated_at: requireString(value, "updated_at", "backlog item")
|
|
4577
|
+
};
|
|
4578
|
+
}
|
|
4579
|
+
function parseDiffFileListWire(payload) {
|
|
4580
|
+
if (!isRecord(payload)) throw new Error("diff file list must be an object");
|
|
4581
|
+
if (!Array.isArray(payload.files)) throw new Error('diff file list is missing "files"');
|
|
4582
|
+
const files = payload.files.map((file, index) => {
|
|
4583
|
+
if (!isRecord(file)) throw new Error(`diff file entry ${index} must be an object`);
|
|
4584
|
+
const status = requireString(file, "status", `diff file entry ${index}`);
|
|
4585
|
+
if (!DIFF_FILE_STATUSES.includes(status)) throw new Error(`diff file entry ${index} has an invalid "status"`);
|
|
4586
|
+
return {
|
|
4587
|
+
path: requireString(file, "path", `diff file entry ${index}`),
|
|
4588
|
+
status,
|
|
4589
|
+
insertions: requireNumber(file, "insertions", `diff file entry ${index}`),
|
|
4590
|
+
deletions: requireNumber(file, "deletions", `diff file entry ${index}`),
|
|
4591
|
+
...typeof file.oldPath === "string" ? { oldPath: file.oldPath } : {},
|
|
4592
|
+
binary: requireBoolean(file, "binary", `diff file entry ${index}`)
|
|
4593
|
+
};
|
|
4594
|
+
});
|
|
4595
|
+
return {
|
|
4596
|
+
files,
|
|
4597
|
+
totalInsertions: requireNumber(payload, "totalInsertions", "diff file list"),
|
|
4598
|
+
totalDeletions: requireNumber(payload, "totalDeletions", "diff file list")
|
|
4599
|
+
};
|
|
4600
|
+
}
|
|
4601
|
+
function parseDiffFileContentWire(payload) {
|
|
4602
|
+
if (!isRecord(payload)) throw new Error("diff file content must be an object");
|
|
4603
|
+
return {
|
|
4604
|
+
original: requireString(payload, "original", "diff file content"),
|
|
4605
|
+
modified: requireString(payload, "modified", "diff file content"),
|
|
4606
|
+
language: requireString(payload, "language", "diff file content")
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4610
|
+
// src/wire/payloads.ts
|
|
4611
|
+
function parseReadStreamResponsePayload(payload) {
|
|
4612
|
+
if (!isRecord(payload)) throw new Error("read-stream response must be an object");
|
|
4613
|
+
if (typeof payload.scrollback !== "string") throw new Error('read-stream response is missing "scrollback"');
|
|
4614
|
+
if (!isRecord(payload.activity)) throw new Error('read-stream response is missing "activity"');
|
|
4615
|
+
const state = payload.activity.state;
|
|
4616
|
+
const reason = payload.activity.reason;
|
|
4617
|
+
if (state !== null && !isActivityStateWire(state)) throw new Error('read-stream response has an invalid activity "state"');
|
|
4618
|
+
if (reason !== null && !isActivityReasonWire(reason)) throw new Error('read-stream response has an invalid activity "reason"');
|
|
4619
|
+
if (payload.awaitedPromptId !== null && typeof payload.awaitedPromptId !== "string") {
|
|
4620
|
+
throw new Error('read-stream response has an invalid "awaitedPromptId"');
|
|
4621
|
+
}
|
|
4622
|
+
return {
|
|
4623
|
+
scrollback: payload.scrollback,
|
|
4624
|
+
activity: { state: state ?? null, reason: reason ?? null },
|
|
4625
|
+
usage: payload.usage === null || payload.usage === void 0 ? null : parseSessionUsageWire(payload.usage),
|
|
4626
|
+
awaitedPromptId: payload.awaitedPromptId ?? null
|
|
4627
|
+
};
|
|
4628
|
+
}
|
|
4629
|
+
function parseReadStreamRequestPayload(payload) {
|
|
4630
|
+
if (!isRecord(payload)) throw new Error("read-stream payload must be an object");
|
|
4631
|
+
if (typeof payload.sessionId !== "string") throw new Error('read-stream payload missing "sessionId"');
|
|
4632
|
+
if (payload.action !== "subscribe" && payload.action !== "unsubscribe") {
|
|
4633
|
+
throw new Error('read-stream payload has an invalid "action"');
|
|
4634
|
+
}
|
|
4635
|
+
return { sessionId: payload.sessionId, action: payload.action };
|
|
4636
|
+
}
|
|
4637
|
+
function parseReadBoardResponsePayload(payload) {
|
|
4638
|
+
if (!isRecord(payload)) throw new Error("read-board response must be an object");
|
|
4639
|
+
if (Array.isArray(payload.projects)) {
|
|
4640
|
+
const projects = payload.projects.map((project, index) => {
|
|
4641
|
+
if (!isRecord(project) || typeof project.id !== "string" || typeof project.name !== "string") {
|
|
4642
|
+
throw new Error(`read-board project ${index} is malformed`);
|
|
4643
|
+
}
|
|
4644
|
+
return { id: project.id, name: project.name };
|
|
4645
|
+
});
|
|
4646
|
+
return { projects };
|
|
4647
|
+
}
|
|
4648
|
+
if (typeof payload.projectId !== "string") throw new Error('read-board response is missing "projectId"');
|
|
4649
|
+
if (!Array.isArray(payload.columns)) throw new Error('read-board response is missing "columns"');
|
|
4650
|
+
if (!Array.isArray(payload.tasks)) throw new Error('read-board response is missing "tasks"');
|
|
4651
|
+
if (!Array.isArray(payload.backlog)) throw new Error('read-board response is missing "backlog"');
|
|
4652
|
+
return {
|
|
4653
|
+
projectId: payload.projectId,
|
|
4654
|
+
columns: payload.columns.map((column) => parseBoardColumnWire(column)),
|
|
4655
|
+
tasks: payload.tasks.map((task) => parseBoardTaskWire(task)),
|
|
4656
|
+
backlog: payload.backlog.map((item) => parseBacklogItemWire(item))
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
function parseReadBoardRequestPayload(payload) {
|
|
4660
|
+
if (!isRecord(payload)) throw new Error("read-board payload must be an object");
|
|
4661
|
+
if (payload.projectId !== void 0 && typeof payload.projectId !== "string") {
|
|
4662
|
+
throw new Error('read-board payload has a non-string "projectId"');
|
|
4663
|
+
}
|
|
4664
|
+
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
|
|
4665
|
+
throw new Error('read-board payload has an invalid "action"');
|
|
4666
|
+
}
|
|
4667
|
+
return { projectId: payload.projectId, action: payload.action };
|
|
4668
|
+
}
|
|
4669
|
+
function parseReadDiffResponsePayload(payload) {
|
|
4670
|
+
if (!isRecord(payload)) throw new Error("read-diff response must be an object");
|
|
4671
|
+
return "files" in payload ? parseDiffFileListWire(payload) : parseDiffFileContentWire(payload);
|
|
4672
|
+
}
|
|
4673
|
+
function parseReadDiffRequestPayload(payload) {
|
|
4674
|
+
if (!isRecord(payload)) throw new Error("read-diff payload must be an object");
|
|
4675
|
+
if (typeof payload.taskId !== "string") throw new Error('read-diff payload missing "taskId"');
|
|
4676
|
+
if (typeof payload.projectId !== "string") throw new Error('read-diff payload missing "projectId"');
|
|
4677
|
+
if (payload.filePath !== void 0 && typeof payload.filePath !== "string") {
|
|
4678
|
+
throw new Error('read-diff payload has a non-string "filePath"');
|
|
4679
|
+
}
|
|
4680
|
+
if (payload.scope !== void 0 && payload.scope !== "working" && payload.scope !== "staged" && payload.scope !== "branch") {
|
|
4681
|
+
throw new Error('read-diff payload has an invalid "scope"');
|
|
4682
|
+
}
|
|
4683
|
+
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
|
|
4684
|
+
throw new Error('read-diff payload has an invalid "action"');
|
|
4685
|
+
}
|
|
4686
|
+
return {
|
|
4687
|
+
taskId: payload.taskId,
|
|
4688
|
+
projectId: payload.projectId,
|
|
4689
|
+
filePath: payload.filePath,
|
|
4690
|
+
scope: payload.scope,
|
|
4691
|
+
action: payload.action
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
function parseSendUserMessageRequestPayload(payload) {
|
|
4695
|
+
if (!isRecord(payload)) throw new Error("send-user-message payload must be an object");
|
|
4696
|
+
if (typeof payload.sessionId !== "string") throw new Error('send-user-message payload missing "sessionId"');
|
|
4697
|
+
if (typeof payload.text !== "string") throw new Error('send-user-message payload missing "text"');
|
|
4698
|
+
return { sessionId: payload.sessionId, text: payload.text };
|
|
4699
|
+
}
|
|
4700
|
+
function parseMoveTaskRequestPayload(payload) {
|
|
4701
|
+
if (!isRecord(payload)) throw new Error("move-task payload must be an object");
|
|
4702
|
+
if (typeof payload.taskId !== "string") throw new Error('move-task payload missing "taskId"');
|
|
4703
|
+
if (typeof payload.targetSwimlaneId !== "string") throw new Error('move-task payload missing "targetSwimlaneId"');
|
|
4704
|
+
if (typeof payload.targetPosition !== "number") throw new Error('move-task payload missing "targetPosition"');
|
|
4705
|
+
if (typeof payload.projectId !== "string") throw new Error('move-task payload missing "projectId"');
|
|
4706
|
+
return {
|
|
4707
|
+
taskId: payload.taskId,
|
|
4708
|
+
targetSwimlaneId: payload.targetSwimlaneId,
|
|
4709
|
+
targetPosition: payload.targetPosition,
|
|
4710
|
+
projectId: payload.projectId
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
function parseAnswerPermissionPromptRequestPayload(payload) {
|
|
4714
|
+
if (!isRecord(payload)) throw new Error("answer-permission-prompt payload must be an object");
|
|
4715
|
+
if (typeof payload.sessionId !== "string") throw new Error('answer-permission-prompt payload missing "sessionId"');
|
|
4716
|
+
if (typeof payload.promptId !== "string") throw new Error('answer-permission-prompt payload missing "promptId"');
|
|
4717
|
+
if (typeof payload.keystrokes !== "string") throw new Error('answer-permission-prompt payload missing "keystrokes"');
|
|
4718
|
+
return { sessionId: payload.sessionId, promptId: payload.promptId, keystrokes: payload.keystrokes };
|
|
4719
|
+
}
|
|
4720
|
+
function parseInteractiveTerminalRequestPayload(payload) {
|
|
4721
|
+
if (!isRecord(payload)) throw new Error("interactive-terminal payload must be an object");
|
|
4722
|
+
if (typeof payload.sessionId !== "string") throw new Error('interactive-terminal payload missing "sessionId"');
|
|
4723
|
+
if (typeof payload.data !== "string") throw new Error('interactive-terminal payload missing "data"');
|
|
4724
|
+
return { sessionId: payload.sessionId, data: payload.data };
|
|
4725
|
+
}
|
|
4726
|
+
function parseBoardToolRequestPayload(payload) {
|
|
4727
|
+
if (!isRecord(payload)) throw new Error("board-tool payload must be an object");
|
|
4728
|
+
if (typeof payload.tool !== "string") throw new Error('board-tool payload missing "tool"');
|
|
4729
|
+
if (payload.params === void 0 || !isJsonValue(payload.params)) throw new Error('board-tool payload missing a JSON "params"');
|
|
4730
|
+
return { tool: payload.tool, params: payload.params };
|
|
4731
|
+
}
|
|
4732
|
+
function parseCapabilityRequestPayload(verb, payload) {
|
|
4733
|
+
switch (verb) {
|
|
4734
|
+
case "read-stream":
|
|
4735
|
+
return parseReadStreamRequestPayload(payload);
|
|
4736
|
+
case "read-board":
|
|
4737
|
+
return parseReadBoardRequestPayload(payload);
|
|
4738
|
+
case "read-diff":
|
|
4739
|
+
return parseReadDiffRequestPayload(payload);
|
|
4740
|
+
case "send-user-message":
|
|
4741
|
+
return parseSendUserMessageRequestPayload(payload);
|
|
4742
|
+
case "move-task":
|
|
4743
|
+
return parseMoveTaskRequestPayload(payload);
|
|
4744
|
+
case "answer-permission-prompt":
|
|
4745
|
+
return parseAnswerPermissionPromptRequestPayload(payload);
|
|
4746
|
+
case "interactive-terminal":
|
|
4747
|
+
return parseInteractiveTerminalRequestPayload(payload);
|
|
4748
|
+
case "board-tool-read":
|
|
4749
|
+
case "board-tool-write":
|
|
4750
|
+
return parseBoardToolRequestPayload(payload);
|
|
4751
|
+
default: {
|
|
4752
|
+
const exhaustiveCheck = verb;
|
|
4753
|
+
throw new Error(`Unknown capability verb: ${String(exhaustiveCheck)}`);
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
|
|
4354
4758
|
// src/wire/base64url.ts
|
|
4355
4759
|
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
4356
4760
|
var DECODE_MAP = {};
|
|
@@ -4512,8 +4916,108 @@ function rosterDeviceCapabilitySet(entry) {
|
|
|
4512
4916
|
function capabilitySetToRosterCapabilities(capabilities) {
|
|
4513
4917
|
return capabilitySetToArray(capabilities);
|
|
4514
4918
|
}
|
|
4919
|
+
|
|
4920
|
+
// src/capabilities/board-tools.ts
|
|
4921
|
+
var BOARD_TOOL_READ_NAMES = [
|
|
4922
|
+
"search_tasks",
|
|
4923
|
+
"find_task",
|
|
4924
|
+
"get_current_task",
|
|
4925
|
+
"get_task_stats",
|
|
4926
|
+
"get_usage_stats",
|
|
4927
|
+
"board_summary",
|
|
4928
|
+
"list_sessions",
|
|
4929
|
+
"get_session_history",
|
|
4930
|
+
"get_column_detail",
|
|
4931
|
+
"get_handoff_context",
|
|
4932
|
+
"get_transcript",
|
|
4933
|
+
"get_session_files",
|
|
4934
|
+
"get_session_events"
|
|
4935
|
+
];
|
|
4936
|
+
var BOARD_TOOL_WRITE_NAMES = [
|
|
4937
|
+
"create_task",
|
|
4938
|
+
"update_task",
|
|
4939
|
+
"delete_task",
|
|
4940
|
+
"link_pr",
|
|
4941
|
+
"remove_attachment",
|
|
4942
|
+
"update_column",
|
|
4943
|
+
"create_backlog_task",
|
|
4944
|
+
"promote_backlog",
|
|
4945
|
+
"update_backlog_item",
|
|
4946
|
+
"delete_backlog_item"
|
|
4947
|
+
];
|
|
4948
|
+
function isBoardToolReadName(value) {
|
|
4949
|
+
return BOARD_TOOL_READ_NAMES.includes(value);
|
|
4950
|
+
}
|
|
4951
|
+
function isBoardToolWriteName(value) {
|
|
4952
|
+
return BOARD_TOOL_WRITE_NAMES.includes(value);
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
// src/events/event.ts
|
|
4956
|
+
var BOARD_CHANGES = ["task-created", "task-updated", "task-deleted", "swimlane-updated", "backlog-changed"];
|
|
4957
|
+
function parseActivityEventPayload(payload) {
|
|
4958
|
+
if (!isRecord(payload)) throw new Error("activity payload must be an object");
|
|
4959
|
+
switch (payload.type) {
|
|
4960
|
+
case "activity": {
|
|
4961
|
+
if (!isActivityStateWire(payload.state)) throw new Error('activity payload has an invalid "state"');
|
|
4962
|
+
if (!isActivityReasonWire(payload.reason)) throw new Error('activity payload has an invalid "reason"');
|
|
4963
|
+
return { type: "activity", state: payload.state, reason: payload.reason };
|
|
4964
|
+
}
|
|
4965
|
+
case "usage":
|
|
4966
|
+
return { type: "usage", usage: parseSessionUsageWire(payload.usage) };
|
|
4967
|
+
case "event":
|
|
4968
|
+
return { type: "event", event: parseSessionEventWire(payload.event) };
|
|
4969
|
+
case "permission": {
|
|
4970
|
+
if (typeof payload.promptId !== "string") throw new Error('permission payload is missing "promptId"');
|
|
4971
|
+
if (typeof payload.pending !== "boolean") throw new Error('permission payload is missing "pending"');
|
|
4972
|
+
return { type: "permission", promptId: payload.promptId, pending: payload.pending };
|
|
4973
|
+
}
|
|
4974
|
+
default:
|
|
4975
|
+
throw new Error('activity payload has an unknown "type"');
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
function isBridgeEvent(value) {
|
|
4979
|
+
if (!isRecord(value)) return false;
|
|
4980
|
+
switch (value.kind) {
|
|
4981
|
+
case "transcript": {
|
|
4982
|
+
if (typeof value.sessionId !== "string" || typeof value.taskId !== "string") return false;
|
|
4983
|
+
try {
|
|
4984
|
+
parseTranscriptEntriesWire(value.payload);
|
|
4985
|
+
return true;
|
|
4986
|
+
} catch {
|
|
4987
|
+
return false;
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
case "activity": {
|
|
4991
|
+
if (typeof value.sessionId !== "string" || typeof value.taskId !== "string") return false;
|
|
4992
|
+
try {
|
|
4993
|
+
parseActivityEventPayload(value.payload);
|
|
4994
|
+
return true;
|
|
4995
|
+
} catch {
|
|
4996
|
+
return false;
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
case "terminal": {
|
|
5000
|
+
if (typeof value.sessionId !== "string" || typeof value.taskId !== "string") return false;
|
|
5001
|
+
return isRecord(value.payload) && typeof value.payload.data === "string";
|
|
5002
|
+
}
|
|
5003
|
+
case "board": {
|
|
5004
|
+
if (typeof value.projectId !== "string") return false;
|
|
5005
|
+
if (value.taskId !== void 0 && typeof value.taskId !== "string") return false;
|
|
5006
|
+
if (!isRecord(value.payload)) return false;
|
|
5007
|
+
const change = value.payload.change;
|
|
5008
|
+
if (typeof change !== "string" || !BOARD_CHANGES.includes(change)) return false;
|
|
5009
|
+
return Array.isArray(value.payload.ids) && value.payload.ids.every((id) => typeof id === "string");
|
|
5010
|
+
}
|
|
5011
|
+
case "diff":
|
|
5012
|
+
return typeof value.taskId === "string" && value.payload === null;
|
|
5013
|
+
default:
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
4515
5017
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4516
5018
|
0 && (module.exports = {
|
|
5019
|
+
BOARD_TOOL_READ_NAMES,
|
|
5020
|
+
BOARD_TOOL_WRITE_NAMES,
|
|
4517
5021
|
CAPABILITY_VERBS,
|
|
4518
5022
|
CipherState,
|
|
4519
5023
|
ED25519_KEY_LENGTH,
|
|
@@ -4537,6 +5041,7 @@ function capabilitySetToRosterCapabilities(capabilities) {
|
|
|
4537
5041
|
decodeMessage,
|
|
4538
5042
|
decodePairingQrPayload,
|
|
4539
5043
|
deriveSecretstreamPair,
|
|
5044
|
+
deriveSessionSlotId,
|
|
4540
5045
|
deriveShortAuthenticationString,
|
|
4541
5046
|
encodeMessage,
|
|
4542
5047
|
encodePairingQrPayload,
|
|
@@ -4546,8 +5051,28 @@ function capabilitySetToRosterCapabilities(capabilities) {
|
|
|
4546
5051
|
generateEd25519KeyPair,
|
|
4547
5052
|
generateX25519KeyPair,
|
|
4548
5053
|
hexToBytes,
|
|
5054
|
+
isActivityReasonWire,
|
|
5055
|
+
isActivityStateWire,
|
|
5056
|
+
isBoardToolReadName,
|
|
5057
|
+
isBoardToolWriteName,
|
|
5058
|
+
isBridgeEvent,
|
|
4549
5059
|
isCapabilityVerb,
|
|
5060
|
+
isJsonValue,
|
|
5061
|
+
isRecord,
|
|
4550
5062
|
isRosterEntryExpired,
|
|
5063
|
+
parseActivityEventPayload,
|
|
5064
|
+
parseBacklogItemWire,
|
|
5065
|
+
parseBoardColumnWire,
|
|
5066
|
+
parseBoardTaskWire,
|
|
5067
|
+
parseCapabilityRequestPayload,
|
|
5068
|
+
parseDiffFileContentWire,
|
|
5069
|
+
parseDiffFileListWire,
|
|
5070
|
+
parseReadBoardResponsePayload,
|
|
5071
|
+
parseReadDiffResponsePayload,
|
|
5072
|
+
parseReadStreamResponsePayload,
|
|
5073
|
+
parseSessionEventWire,
|
|
5074
|
+
parseSessionUsageWire,
|
|
5075
|
+
parseTranscriptEntriesWire,
|
|
4551
5076
|
randomBytes,
|
|
4552
5077
|
rosterDeviceCapabilitySet,
|
|
4553
5078
|
signRosterEntry,
|