@kangentic/protocol 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/board-tools.d.ts +2 -2
- package/dist/events/payloads.d.ts +48 -0
- package/dist/index.cjs +71 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +70 -4
- package/dist/wire/payloads.d.ts +71 -5
- package/package.json +2 -1
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* this protocol surface honest: a tool renamed or removed desktop-side
|
|
14
14
|
* fails that test instead of silently breaking phones.
|
|
15
15
|
*/
|
|
16
|
-
export declare const BOARD_TOOL_READ_NAMES: readonly ["search_tasks", "find_task", "get_current_task", "get_task_stats", "get_usage_stats", "board_summary", "list_sessions", "get_session_history", "get_column_detail", "get_handoff_context", "get_transcript", "get_session_files", "get_session_events", "get_activity_intervals"];
|
|
17
|
-
export declare const BOARD_TOOL_WRITE_NAMES: readonly ["create_task", "update_task", "delete_task", "link_pr", "remove_attachment", "update_column", "create_backlog_task", "promote_backlog", "update_backlog_item", "delete_backlog_item"];
|
|
16
|
+
export declare const BOARD_TOOL_READ_NAMES: readonly ["search_tasks", "find_task", "get_current_task", "get_task_stats", "get_usage_stats", "board_summary", "list_sessions", "get_session_history", "get_column_detail", "list_board_profiles", "get_handoff_context", "get_transcript", "get_session_files", "get_session_events", "get_activity_intervals"];
|
|
17
|
+
export declare const BOARD_TOOL_WRITE_NAMES: readonly ["create_task", "update_task", "delete_task", "link_pr", "remove_attachment", "update_column", "create_board_profile", "update_board_profile", "delete_board_profile", "create_backlog_task", "promote_backlog", "update_backlog_item", "delete_backlog_item"];
|
|
18
18
|
export type BoardToolReadName = (typeof BOARD_TOOL_READ_NAMES)[number];
|
|
19
19
|
export type BoardToolWriteName = (typeof BOARD_TOOL_WRITE_NAMES)[number];
|
|
20
20
|
export type BoardToolName = BoardToolReadName | BoardToolWriteName;
|
|
@@ -167,6 +167,44 @@ export interface SessionUsageWire {
|
|
|
167
167
|
effort?: string;
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Phone-needed subset of the desktop's SessionSummary: what a task COST over
|
|
172
|
+
* its whole life, as opposed to SessionUsageWire's live snapshot of one
|
|
173
|
+
* running agent.
|
|
174
|
+
*
|
|
175
|
+
* Every figure is a lifetime aggregate across every session record of the
|
|
176
|
+
* task, which is what makes it meaningful for a completed one: a task worked
|
|
177
|
+
* across five `--resume` legs has five session rows, and reporting only the
|
|
178
|
+
* final leg would under-report everything. The desktop's getSummaryForTask
|
|
179
|
+
* owns those aggregation rules (SUM cost/duration/tool calls/lines, MAX files
|
|
180
|
+
* changed, and tokens taken as the latest row per session lineage so a
|
|
181
|
+
* resumed session is not double-counted) - they are not re-derived here.
|
|
182
|
+
*
|
|
183
|
+
* `sessionId` is the transcript ANCHOR, not a live session: it resolves
|
|
184
|
+
* through the desktop's session records, which outlive the agent, so it reads
|
|
185
|
+
* a finished task's conversation with nothing running. An archived task's
|
|
186
|
+
* `session_id` on the board is null by then, so this is the only handle to it.
|
|
187
|
+
*/
|
|
188
|
+
export interface SessionSummaryWire {
|
|
189
|
+
sessionId: string;
|
|
190
|
+
totalCostUsd: number;
|
|
191
|
+
totalInputTokens: number;
|
|
192
|
+
totalOutputTokens: number;
|
|
193
|
+
modelDisplayName: string;
|
|
194
|
+
durationMs: number;
|
|
195
|
+
toolCallCount: number;
|
|
196
|
+
compactionCount: number;
|
|
197
|
+
linesAdded: number;
|
|
198
|
+
linesRemoved: number;
|
|
199
|
+
filesChanged: number;
|
|
200
|
+
/** ISO 8601. The task's creation, which is where its timeline starts. */
|
|
201
|
+
taskCreatedAt: string;
|
|
202
|
+
/** ISO 8601. Earliest session start across every leg. */
|
|
203
|
+
startedAt: string;
|
|
204
|
+
/** ISO 8601, or null while a leg is still open. Latest exit or suspend. */
|
|
205
|
+
exitedAt: string | null;
|
|
206
|
+
exitCode: number | null;
|
|
207
|
+
}
|
|
170
208
|
/**
|
|
171
209
|
* Loose mirror of the desktop's SessionEvent telemetry entry. `type` is
|
|
172
210
|
* deliberately a plain string, not the desktop's EventType enum, so a new
|
|
@@ -267,6 +305,16 @@ export declare function isActivityStateWire(value: unknown): value is ActivitySt
|
|
|
267
305
|
export declare function isActivityReasonWire(value: unknown): value is ActivityReasonWire;
|
|
268
306
|
/** Narrows a usage payload to the SessionUsageWire subset. Throws on a malformed required field. */
|
|
269
307
|
export declare function parseSessionUsageWire(payload: JsonValue): SessionUsageWire;
|
|
308
|
+
/**
|
|
309
|
+
* Narrows a session-summary payload to SessionSummaryWire. Throws on a
|
|
310
|
+
* malformed required field.
|
|
311
|
+
*
|
|
312
|
+
* The numeric metrics are required rather than optional because the desktop
|
|
313
|
+
* COALESCEs each to 0 before it sends them, so "no data" arrives as a real
|
|
314
|
+
* zero. Making them optional here would invite a phone to render an em-dash
|
|
315
|
+
* where the honest answer is 0.
|
|
316
|
+
*/
|
|
317
|
+
export declare function parseSessionSummaryWire(payload: JsonValue): SessionSummaryWire;
|
|
270
318
|
/** Narrows a session-event payload to SessionEventWire. Throws on a malformed required field. */
|
|
271
319
|
export declare function parseSessionEventWire(payload: JsonValue): SessionEventWire;
|
|
272
320
|
/** Narrows one board-column row. Throws on a malformed required field. */
|
package/dist/index.cjs
CHANGED
|
@@ -88,6 +88,7 @@ __export(index_exports, {
|
|
|
88
88
|
parseReadStreamResponsePayload: () => parseReadStreamResponsePayload,
|
|
89
89
|
parseRegisterPushRequestPayload: () => parseRegisterPushRequestPayload,
|
|
90
90
|
parseSessionEventWire: () => parseSessionEventWire,
|
|
91
|
+
parseSessionSummaryWire: () => parseSessionSummaryWire,
|
|
91
92
|
parseSessionUsageWire: () => parseSessionUsageWire,
|
|
92
93
|
parseTerminalDimensionsWire: () => parseTerminalDimensionsWire,
|
|
93
94
|
parseTranscriptEntriesWire: () => parseTranscriptEntriesWire,
|
|
@@ -5364,6 +5365,26 @@ function parseSessionUsageWire(payload) {
|
|
|
5364
5365
|
}
|
|
5365
5366
|
};
|
|
5366
5367
|
}
|
|
5368
|
+
function parseSessionSummaryWire(payload) {
|
|
5369
|
+
if (!isRecord(payload)) throw new Error("session summary payload must be an object");
|
|
5370
|
+
return {
|
|
5371
|
+
sessionId: requireString(payload, "sessionId", "session summary"),
|
|
5372
|
+
totalCostUsd: requireNumber(payload, "totalCostUsd", "session summary"),
|
|
5373
|
+
totalInputTokens: requireNumber(payload, "totalInputTokens", "session summary"),
|
|
5374
|
+
totalOutputTokens: requireNumber(payload, "totalOutputTokens", "session summary"),
|
|
5375
|
+
modelDisplayName: requireString(payload, "modelDisplayName", "session summary"),
|
|
5376
|
+
durationMs: requireNumber(payload, "durationMs", "session summary"),
|
|
5377
|
+
toolCallCount: requireNumber(payload, "toolCallCount", "session summary"),
|
|
5378
|
+
compactionCount: requireNumber(payload, "compactionCount", "session summary"),
|
|
5379
|
+
linesAdded: requireNumber(payload, "linesAdded", "session summary"),
|
|
5380
|
+
linesRemoved: requireNumber(payload, "linesRemoved", "session summary"),
|
|
5381
|
+
filesChanged: requireNumber(payload, "filesChanged", "session summary"),
|
|
5382
|
+
taskCreatedAt: requireString(payload, "taskCreatedAt", "session summary"),
|
|
5383
|
+
startedAt: requireString(payload, "startedAt", "session summary"),
|
|
5384
|
+
exitedAt: typeof payload.exitedAt === "string" ? payload.exitedAt : null,
|
|
5385
|
+
exitCode: typeof payload.exitCode === "number" ? payload.exitCode : null
|
|
5386
|
+
};
|
|
5387
|
+
}
|
|
5367
5388
|
function parseSessionEventWire(payload) {
|
|
5368
5389
|
if (!isRecord(payload)) throw new Error("session event payload must be an object");
|
|
5369
5390
|
return {
|
|
@@ -5566,6 +5587,18 @@ function parseAccentColor(value, context) {
|
|
|
5566
5587
|
}
|
|
5567
5588
|
function parseReadBoardResponsePayload(payload) {
|
|
5568
5589
|
if (!isRecord(payload)) throw new Error("read-board response must be an object");
|
|
5590
|
+
if (Array.isArray(payload.archivedTasks)) {
|
|
5591
|
+
if (typeof payload.projectId !== "string") throw new Error('read-board archived response is missing "projectId"');
|
|
5592
|
+
if (typeof payload.archivedTotalCount !== "number") {
|
|
5593
|
+
throw new Error('read-board archived response is missing "archivedTotalCount"');
|
|
5594
|
+
}
|
|
5595
|
+
return {
|
|
5596
|
+
projectId: payload.projectId,
|
|
5597
|
+
archivedTasks: payload.archivedTasks.map((task) => parseBoardTaskWire(task)),
|
|
5598
|
+
archivedTotalCount: payload.archivedTotalCount,
|
|
5599
|
+
summariesByTaskId: parseSummariesByTaskId(payload.summariesByTaskId)
|
|
5600
|
+
};
|
|
5601
|
+
}
|
|
5569
5602
|
if (Array.isArray(payload.projects)) {
|
|
5570
5603
|
const projects = payload.projects.map((project, index) => {
|
|
5571
5604
|
if (!isRecord(project) || typeof project.id !== "string" || typeof project.name !== "string") {
|
|
@@ -5574,10 +5607,15 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5574
5607
|
return {
|
|
5575
5608
|
id: project.id,
|
|
5576
5609
|
name: project.name,
|
|
5577
|
-
...project.color !== void 0 ? { color: parseAccentColor(project.color, `read-board project ${index}`) } : {}
|
|
5610
|
+
...project.color !== void 0 ? { color: parseAccentColor(project.color, `read-board project ${index}`) } : {},
|
|
5611
|
+
...typeof project.groupId === "string" ? { groupId: project.groupId } : {},
|
|
5612
|
+
...typeof project.position === "number" ? { position: project.position } : {}
|
|
5578
5613
|
};
|
|
5579
5614
|
});
|
|
5580
|
-
|
|
5615
|
+
const groups = Array.isArray(payload.groups) ? payload.groups.flatMap(
|
|
5616
|
+
(group) => isRecord(group) && typeof group.id === "string" && typeof group.name === "string" && typeof group.position === "number" ? [{ id: group.id, name: group.name, position: group.position }] : []
|
|
5617
|
+
) : void 0;
|
|
5618
|
+
return { projects, ...groups !== void 0 ? { groups } : {} };
|
|
5581
5619
|
}
|
|
5582
5620
|
if (typeof payload.projectId !== "string") throw new Error('read-board response is missing "projectId"');
|
|
5583
5621
|
if (!Array.isArray(payload.columns)) throw new Error('read-board response is missing "columns"');
|
|
@@ -5610,18 +5648,42 @@ function parseTaskCountsByColumnId(value) {
|
|
|
5610
5648
|
}
|
|
5611
5649
|
return counts;
|
|
5612
5650
|
}
|
|
5651
|
+
function parseSummariesByTaskId(value) {
|
|
5652
|
+
if (value === void 0) return {};
|
|
5653
|
+
if (!isRecord(value)) throw new Error('read-board archived response has a non-object "summariesByTaskId"');
|
|
5654
|
+
const summaries = {};
|
|
5655
|
+
for (const [taskId, summary] of Object.entries(value)) {
|
|
5656
|
+
try {
|
|
5657
|
+
summaries[taskId] = parseSessionSummaryWire(summary);
|
|
5658
|
+
} catch {
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
return summaries;
|
|
5662
|
+
}
|
|
5613
5663
|
function parseReadBoardRequestPayload(payload) {
|
|
5614
5664
|
if (!isRecord(payload)) throw new Error("read-board payload must be an object");
|
|
5615
5665
|
if (payload.projectId !== void 0 && typeof payload.projectId !== "string") {
|
|
5616
5666
|
throw new Error('read-board payload has a non-string "projectId"');
|
|
5617
5667
|
}
|
|
5618
|
-
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
|
|
5668
|
+
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe" && payload.action !== "archived") {
|
|
5619
5669
|
throw new Error('read-board payload has an invalid "action"');
|
|
5620
5670
|
}
|
|
5621
5671
|
if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
|
|
5622
5672
|
throw new Error('read-board payload has an invalid "view"');
|
|
5623
5673
|
}
|
|
5624
|
-
|
|
5674
|
+
if (payload.limit !== void 0 && (typeof payload.limit !== "number" || !Number.isInteger(payload.limit) || payload.limit < 1)) {
|
|
5675
|
+
throw new Error('read-board payload has an invalid "limit"');
|
|
5676
|
+
}
|
|
5677
|
+
if (payload.offset !== void 0 && (typeof payload.offset !== "number" || !Number.isInteger(payload.offset) || payload.offset < 0)) {
|
|
5678
|
+
throw new Error('read-board payload has an invalid "offset"');
|
|
5679
|
+
}
|
|
5680
|
+
return {
|
|
5681
|
+
projectId: payload.projectId,
|
|
5682
|
+
action: payload.action,
|
|
5683
|
+
view: payload.view,
|
|
5684
|
+
limit: payload.limit,
|
|
5685
|
+
offset: payload.offset
|
|
5686
|
+
};
|
|
5625
5687
|
}
|
|
5626
5688
|
function parseReadDiffResponsePayload(payload) {
|
|
5627
5689
|
if (!isRecord(payload)) throw new Error("read-diff response must be an object");
|
|
@@ -5942,6 +6004,7 @@ var BOARD_TOOL_READ_NAMES = [
|
|
|
5942
6004
|
"list_sessions",
|
|
5943
6005
|
"get_session_history",
|
|
5944
6006
|
"get_column_detail",
|
|
6007
|
+
"list_board_profiles",
|
|
5945
6008
|
"get_handoff_context",
|
|
5946
6009
|
"get_transcript",
|
|
5947
6010
|
"get_session_files",
|
|
@@ -5955,6 +6018,9 @@ var BOARD_TOOL_WRITE_NAMES = [
|
|
|
5955
6018
|
"link_pr",
|
|
5956
6019
|
"remove_attachment",
|
|
5957
6020
|
"update_column",
|
|
6021
|
+
"create_board_profile",
|
|
6022
|
+
"update_board_profile",
|
|
6023
|
+
"delete_board_profile",
|
|
5958
6024
|
"create_backlog_task",
|
|
5959
6025
|
"promote_backlog",
|
|
5960
6026
|
"update_backlog_item",
|
|
@@ -6122,6 +6188,7 @@ function isBridgeEvent(value) {
|
|
|
6122
6188
|
parseReadStreamResponsePayload,
|
|
6123
6189
|
parseRegisterPushRequestPayload,
|
|
6124
6190
|
parseSessionEventWire,
|
|
6191
|
+
parseSessionSummaryWire,
|
|
6125
6192
|
parseSessionUsageWire,
|
|
6126
6193
|
parseTerminalDimensionsWire,
|
|
6127
6194
|
parseTranscriptEntriesWire,
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type { JsonValue, BridgeMessage, HeartbeatMessage, CapabilityRequestMessa
|
|
|
12
12
|
export { encodeMessage, decodeMessage, 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, type CapabilityRequestPayloadMap, type CapabilityResponsePayloadMap, type ReadStreamRequestPayload, type ReadStreamResponsePayload, type ReadStreamSessionStatusWire, type TranscriptWindowResponsePayload, type ReadBoardRequestPayload, type ReadBoardView, type ReadBoardProjectSummary, type ReadBoardProjectListResponsePayload, type ReadBoardSnapshotResponsePayload, 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, } from './wire/payloads';
|
|
15
|
+
export { parseCapabilityRequestPayload, parseReadStreamResponsePayload, parseReadBoardResponsePayload, parseReadDiffResponsePayload, parseTranscriptWindowResponsePayload, parseRegisterPushRequestPayload, 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, } from './wire/payloads';
|
|
16
16
|
export { PUSH_CATEGORIES, isPushCategory, parsePushEnvelopePlaintext, sealPushEnvelope, openPushEnvelope, PUSH_ENVELOPE_MAX_AGE_MS, PUSH_ENVELOPE_MAX_FUTURE_SKEW_MS, type PushCategory, type PushEnvelopePlaintext, } from './crypto/push-envelope';
|
|
17
17
|
export { PAIRING_URI_SCHEME, encodePairingQrPayload, decodePairingQrPayload, type PairingQrPayload, } from './pairing/qr';
|
|
18
18
|
export { sealPairingConfirm, openPairingConfirm } from './pairing/confirm';
|
|
@@ -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, parseSessionEventWire, parseTerminalDimensionsWire, parseBoardColumnWire, parseBoardTaskWire, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, type TranscriptBlockWire, type TranscriptEntryWire, type TranscriptTurnUsageWire, type TranscriptSystemSubtypeWire, type ActivityStateWire, type ActivityReasonWire, type SessionUsageWire, type SessionEventWire, type TerminalDimensionsWire, type BoardColumnWire, 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, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, type TranscriptBlockWire, type TranscriptEntryWire, type TranscriptTurnUsageWire, type TranscriptSystemSubtypeWire, type ActivityStateWire, type ActivityReasonWire, type SessionUsageWire, type SessionSummaryWire, type SessionEventWire, type TerminalDimensionsWire, type BoardColumnWire, type BoardTaskWire, 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
|
@@ -5248,6 +5248,26 @@ function parseSessionUsageWire(payload) {
|
|
|
5248
5248
|
}
|
|
5249
5249
|
};
|
|
5250
5250
|
}
|
|
5251
|
+
function parseSessionSummaryWire(payload) {
|
|
5252
|
+
if (!isRecord(payload)) throw new Error("session summary payload must be an object");
|
|
5253
|
+
return {
|
|
5254
|
+
sessionId: requireString(payload, "sessionId", "session summary"),
|
|
5255
|
+
totalCostUsd: requireNumber(payload, "totalCostUsd", "session summary"),
|
|
5256
|
+
totalInputTokens: requireNumber(payload, "totalInputTokens", "session summary"),
|
|
5257
|
+
totalOutputTokens: requireNumber(payload, "totalOutputTokens", "session summary"),
|
|
5258
|
+
modelDisplayName: requireString(payload, "modelDisplayName", "session summary"),
|
|
5259
|
+
durationMs: requireNumber(payload, "durationMs", "session summary"),
|
|
5260
|
+
toolCallCount: requireNumber(payload, "toolCallCount", "session summary"),
|
|
5261
|
+
compactionCount: requireNumber(payload, "compactionCount", "session summary"),
|
|
5262
|
+
linesAdded: requireNumber(payload, "linesAdded", "session summary"),
|
|
5263
|
+
linesRemoved: requireNumber(payload, "linesRemoved", "session summary"),
|
|
5264
|
+
filesChanged: requireNumber(payload, "filesChanged", "session summary"),
|
|
5265
|
+
taskCreatedAt: requireString(payload, "taskCreatedAt", "session summary"),
|
|
5266
|
+
startedAt: requireString(payload, "startedAt", "session summary"),
|
|
5267
|
+
exitedAt: typeof payload.exitedAt === "string" ? payload.exitedAt : null,
|
|
5268
|
+
exitCode: typeof payload.exitCode === "number" ? payload.exitCode : null
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5251
5271
|
function parseSessionEventWire(payload) {
|
|
5252
5272
|
if (!isRecord(payload)) throw new Error("session event payload must be an object");
|
|
5253
5273
|
return {
|
|
@@ -5450,6 +5470,18 @@ function parseAccentColor(value, context) {
|
|
|
5450
5470
|
}
|
|
5451
5471
|
function parseReadBoardResponsePayload(payload) {
|
|
5452
5472
|
if (!isRecord(payload)) throw new Error("read-board response must be an object");
|
|
5473
|
+
if (Array.isArray(payload.archivedTasks)) {
|
|
5474
|
+
if (typeof payload.projectId !== "string") throw new Error('read-board archived response is missing "projectId"');
|
|
5475
|
+
if (typeof payload.archivedTotalCount !== "number") {
|
|
5476
|
+
throw new Error('read-board archived response is missing "archivedTotalCount"');
|
|
5477
|
+
}
|
|
5478
|
+
return {
|
|
5479
|
+
projectId: payload.projectId,
|
|
5480
|
+
archivedTasks: payload.archivedTasks.map((task) => parseBoardTaskWire(task)),
|
|
5481
|
+
archivedTotalCount: payload.archivedTotalCount,
|
|
5482
|
+
summariesByTaskId: parseSummariesByTaskId(payload.summariesByTaskId)
|
|
5483
|
+
};
|
|
5484
|
+
}
|
|
5453
5485
|
if (Array.isArray(payload.projects)) {
|
|
5454
5486
|
const projects = payload.projects.map((project, index) => {
|
|
5455
5487
|
if (!isRecord(project) || typeof project.id !== "string" || typeof project.name !== "string") {
|
|
@@ -5458,10 +5490,15 @@ function parseReadBoardResponsePayload(payload) {
|
|
|
5458
5490
|
return {
|
|
5459
5491
|
id: project.id,
|
|
5460
5492
|
name: project.name,
|
|
5461
|
-
...project.color !== void 0 ? { color: parseAccentColor(project.color, `read-board project ${index}`) } : {}
|
|
5493
|
+
...project.color !== void 0 ? { color: parseAccentColor(project.color, `read-board project ${index}`) } : {},
|
|
5494
|
+
...typeof project.groupId === "string" ? { groupId: project.groupId } : {},
|
|
5495
|
+
...typeof project.position === "number" ? { position: project.position } : {}
|
|
5462
5496
|
};
|
|
5463
5497
|
});
|
|
5464
|
-
|
|
5498
|
+
const groups = Array.isArray(payload.groups) ? payload.groups.flatMap(
|
|
5499
|
+
(group) => isRecord(group) && typeof group.id === "string" && typeof group.name === "string" && typeof group.position === "number" ? [{ id: group.id, name: group.name, position: group.position }] : []
|
|
5500
|
+
) : void 0;
|
|
5501
|
+
return { projects, ...groups !== void 0 ? { groups } : {} };
|
|
5465
5502
|
}
|
|
5466
5503
|
if (typeof payload.projectId !== "string") throw new Error('read-board response is missing "projectId"');
|
|
5467
5504
|
if (!Array.isArray(payload.columns)) throw new Error('read-board response is missing "columns"');
|
|
@@ -5494,18 +5531,42 @@ function parseTaskCountsByColumnId(value) {
|
|
|
5494
5531
|
}
|
|
5495
5532
|
return counts;
|
|
5496
5533
|
}
|
|
5534
|
+
function parseSummariesByTaskId(value) {
|
|
5535
|
+
if (value === void 0) return {};
|
|
5536
|
+
if (!isRecord(value)) throw new Error('read-board archived response has a non-object "summariesByTaskId"');
|
|
5537
|
+
const summaries = {};
|
|
5538
|
+
for (const [taskId, summary] of Object.entries(value)) {
|
|
5539
|
+
try {
|
|
5540
|
+
summaries[taskId] = parseSessionSummaryWire(summary);
|
|
5541
|
+
} catch {
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
return summaries;
|
|
5545
|
+
}
|
|
5497
5546
|
function parseReadBoardRequestPayload(payload) {
|
|
5498
5547
|
if (!isRecord(payload)) throw new Error("read-board payload must be an object");
|
|
5499
5548
|
if (payload.projectId !== void 0 && typeof payload.projectId !== "string") {
|
|
5500
5549
|
throw new Error('read-board payload has a non-string "projectId"');
|
|
5501
5550
|
}
|
|
5502
|
-
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
|
|
5551
|
+
if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe" && payload.action !== "archived") {
|
|
5503
5552
|
throw new Error('read-board payload has an invalid "action"');
|
|
5504
5553
|
}
|
|
5505
5554
|
if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
|
|
5506
5555
|
throw new Error('read-board payload has an invalid "view"');
|
|
5507
5556
|
}
|
|
5508
|
-
|
|
5557
|
+
if (payload.limit !== void 0 && (typeof payload.limit !== "number" || !Number.isInteger(payload.limit) || payload.limit < 1)) {
|
|
5558
|
+
throw new Error('read-board payload has an invalid "limit"');
|
|
5559
|
+
}
|
|
5560
|
+
if (payload.offset !== void 0 && (typeof payload.offset !== "number" || !Number.isInteger(payload.offset) || payload.offset < 0)) {
|
|
5561
|
+
throw new Error('read-board payload has an invalid "offset"');
|
|
5562
|
+
}
|
|
5563
|
+
return {
|
|
5564
|
+
projectId: payload.projectId,
|
|
5565
|
+
action: payload.action,
|
|
5566
|
+
view: payload.view,
|
|
5567
|
+
limit: payload.limit,
|
|
5568
|
+
offset: payload.offset
|
|
5569
|
+
};
|
|
5509
5570
|
}
|
|
5510
5571
|
function parseReadDiffResponsePayload(payload) {
|
|
5511
5572
|
if (!isRecord(payload)) throw new Error("read-diff response must be an object");
|
|
@@ -5826,6 +5887,7 @@ var BOARD_TOOL_READ_NAMES = [
|
|
|
5826
5887
|
"list_sessions",
|
|
5827
5888
|
"get_session_history",
|
|
5828
5889
|
"get_column_detail",
|
|
5890
|
+
"list_board_profiles",
|
|
5829
5891
|
"get_handoff_context",
|
|
5830
5892
|
"get_transcript",
|
|
5831
5893
|
"get_session_files",
|
|
@@ -5839,6 +5901,9 @@ var BOARD_TOOL_WRITE_NAMES = [
|
|
|
5839
5901
|
"link_pr",
|
|
5840
5902
|
"remove_attachment",
|
|
5841
5903
|
"update_column",
|
|
5904
|
+
"create_board_profile",
|
|
5905
|
+
"update_board_profile",
|
|
5906
|
+
"delete_board_profile",
|
|
5842
5907
|
"create_backlog_task",
|
|
5843
5908
|
"promote_backlog",
|
|
5844
5909
|
"update_backlog_item",
|
|
@@ -6005,6 +6070,7 @@ export {
|
|
|
6005
6070
|
parseReadStreamResponsePayload,
|
|
6006
6071
|
parseRegisterPushRequestPayload,
|
|
6007
6072
|
parseSessionEventWire,
|
|
6073
|
+
parseSessionSummaryWire,
|
|
6008
6074
|
parseSessionUsageWire,
|
|
6009
6075
|
parseTerminalDimensionsWire,
|
|
6010
6076
|
parseTranscriptEntriesWire,
|
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 SessionUsageWire, type TerminalDimensionsWire, type TranscriptEntryWire } from '../events/payloads';
|
|
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';
|
|
20
20
|
export interface ReadStreamRequestPayload {
|
|
21
21
|
sessionId: string;
|
|
22
22
|
/**
|
|
@@ -116,8 +116,24 @@ export declare function parseTranscriptWindowResponsePayload(payload: JsonValue)
|
|
|
116
116
|
export type ReadBoardView = 'full' | 'sessions';
|
|
117
117
|
export interface ReadBoardRequestPayload {
|
|
118
118
|
projectId?: string;
|
|
119
|
-
/**
|
|
120
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Defaults to 'subscribe' when omitted. 'unsubscribe' only has an effect
|
|
121
|
+
* when projectId is set - the no-projectId project list is a one-shot read
|
|
122
|
+
* with no live feed to tear down.
|
|
123
|
+
*
|
|
124
|
+
* 'archived' (protocol 0.10.0) is a one-shot page of the project's COMPLETED
|
|
125
|
+
* tasks, which no other projection carries: both 'full' and 'sessions' are
|
|
126
|
+
* built from the desktop's non-archived task list. It is deliberately not a
|
|
127
|
+
* field on the snapshot, because a board subscription re-snapshots on every
|
|
128
|
+
* board change - archived tasks would then repeat down the wire forever,
|
|
129
|
+
* which is the exact bloat the 0.9.0 projections removed. Completed work
|
|
130
|
+
* changes rarely and is read on demand.
|
|
131
|
+
*/
|
|
132
|
+
action?: 'subscribe' | 'unsubscribe' | 'archived';
|
|
133
|
+
/** 'archived' only: page size, newest-archived first. The desktop clamps it. */
|
|
134
|
+
limit?: number;
|
|
135
|
+
/** 'archived' only: how many newest to skip, for paging. Defaults to 0. */
|
|
136
|
+
offset?: number;
|
|
121
137
|
/**
|
|
122
138
|
* subscribe only: the projection wanted (protocol 0.9.0). Omitted means the
|
|
123
139
|
* legacy full board WITH the backlog.
|
|
@@ -135,9 +151,28 @@ export interface ReadBoardRequestPayload {
|
|
|
135
151
|
*/
|
|
136
152
|
view?: ReadBoardView;
|
|
137
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* A desktop project group ("KANGENTIC", "TROY WEB"), for a phone rendering
|
|
156
|
+
* its project list with the same structure the desktop sidebar shows
|
|
157
|
+
* (protocol 0.11.0).
|
|
158
|
+
*/
|
|
159
|
+
export interface ReadBoardProjectGroup {
|
|
160
|
+
id: string;
|
|
161
|
+
name: string;
|
|
162
|
+
/** Display order among groups. */
|
|
163
|
+
position: number;
|
|
164
|
+
}
|
|
138
165
|
export interface ReadBoardProjectSummary {
|
|
139
166
|
id: string;
|
|
140
167
|
name: string;
|
|
168
|
+
/**
|
|
169
|
+
* The group this project belongs to, or null/absent for an ungrouped one
|
|
170
|
+
* (protocol 0.11.0). Absent from an older desktop, which is indistinguishable
|
|
171
|
+
* from ungrouped and renders the same flat list as before.
|
|
172
|
+
*/
|
|
173
|
+
groupId?: string | null;
|
|
174
|
+
/** Display order within the group, mirroring the desktop's own ordering (protocol 0.11.0). */
|
|
175
|
+
position?: number;
|
|
141
176
|
/**
|
|
142
177
|
* Accent color for this project ("#rrggbb"). Today the desktop derives
|
|
143
178
|
* it deterministically from the project id; a user-set project color
|
|
@@ -149,6 +184,12 @@ export interface ReadBoardProjectSummary {
|
|
|
149
184
|
/** Returned when the request omits projectId - the phone's project-bootstrap listing. */
|
|
150
185
|
export interface ReadBoardProjectListResponsePayload {
|
|
151
186
|
projects: ReadBoardProjectSummary[];
|
|
187
|
+
/**
|
|
188
|
+
* The desktop's project groups, in display order (protocol 0.11.0). Absent
|
|
189
|
+
* from an older desktop; a phone that gets none renders one flat list, which
|
|
190
|
+
* is exactly the pre-0.11.0 behaviour.
|
|
191
|
+
*/
|
|
192
|
+
groups?: ReadBoardProjectGroup[];
|
|
152
193
|
}
|
|
153
194
|
/** Returned when the request carries a projectId - a snapshot of that project's board. */
|
|
154
195
|
export interface ReadBoardSnapshotResponsePayload {
|
|
@@ -179,8 +220,33 @@ export interface ReadBoardSnapshotResponsePayload {
|
|
|
179
220
|
*/
|
|
180
221
|
taskCountsByColumnId?: Record<string, number>;
|
|
181
222
|
}
|
|
182
|
-
|
|
183
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Returned for `action: 'archived'` - one page of a project's completed tasks
|
|
225
|
+
* (protocol 0.10.0).
|
|
226
|
+
*
|
|
227
|
+
* These never appear in a board snapshot: the desktop builds those from its
|
|
228
|
+
* non-archived task list, so a phone that only ever subscribed has no way to
|
|
229
|
+
* know a completed task exists at all.
|
|
230
|
+
*/
|
|
231
|
+
export interface ReadBoardArchivedResponsePayload {
|
|
232
|
+
projectId: string;
|
|
233
|
+
/** Newest-archived first. At most the requested `limit`. */
|
|
234
|
+
archivedTasks: BoardTaskWire[];
|
|
235
|
+
/**
|
|
236
|
+
* Total archived tasks in the project, NOT the length of the page above.
|
|
237
|
+
* Drives the column count and tells the phone whether another page exists.
|
|
238
|
+
*/
|
|
239
|
+
archivedTotalCount: number;
|
|
240
|
+
/**
|
|
241
|
+
* Lifetime cost/duration/churn per task id, for the tasks on THIS page that
|
|
242
|
+
* have recorded metrics. Sparse on purpose: a task archived without ever
|
|
243
|
+
* running an agent has nothing to summarize and is simply absent, which is
|
|
244
|
+
* not an error.
|
|
245
|
+
*/
|
|
246
|
+
summariesByTaskId: Record<string, SessionSummaryWire>;
|
|
247
|
+
}
|
|
248
|
+
export type ReadBoardResponsePayload = ReadBoardProjectListResponsePayload | ReadBoardSnapshotResponsePayload | ReadBoardArchivedResponsePayload;
|
|
249
|
+
/** Phone-side narrowing of a read-board response (project list, board snapshot, or archived page). Throws on a malformed required field. */
|
|
184
250
|
export declare function parseReadBoardResponsePayload(payload: JsonValue): ReadBoardResponsePayload;
|
|
185
251
|
/** 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. */
|
|
186
252
|
export type ReadDiffScope = 'working' | 'staged' | 'branch';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kangentic/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"author": {
|
|
27
27
|
"name": "Kangentic",
|
|
28
|
+
"email": "hello@kangentic.com",
|
|
28
29
|
"url": "https://github.com/Kangentic"
|
|
29
30
|
},
|
|
30
31
|
"license": "AGPL-3.0-only",
|