@kangentic/protocol 0.9.0 → 0.10.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.
@@ -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") {
@@ -5610,18 +5643,42 @@ function parseTaskCountsByColumnId(value) {
5610
5643
  }
5611
5644
  return counts;
5612
5645
  }
5646
+ function parseSummariesByTaskId(value) {
5647
+ if (value === void 0) return {};
5648
+ if (!isRecord(value)) throw new Error('read-board archived response has a non-object "summariesByTaskId"');
5649
+ const summaries = {};
5650
+ for (const [taskId, summary] of Object.entries(value)) {
5651
+ try {
5652
+ summaries[taskId] = parseSessionSummaryWire(summary);
5653
+ } catch {
5654
+ }
5655
+ }
5656
+ return summaries;
5657
+ }
5613
5658
  function parseReadBoardRequestPayload(payload) {
5614
5659
  if (!isRecord(payload)) throw new Error("read-board payload must be an object");
5615
5660
  if (payload.projectId !== void 0 && typeof payload.projectId !== "string") {
5616
5661
  throw new Error('read-board payload has a non-string "projectId"');
5617
5662
  }
5618
- if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
5663
+ if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe" && payload.action !== "archived") {
5619
5664
  throw new Error('read-board payload has an invalid "action"');
5620
5665
  }
5621
5666
  if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
5622
5667
  throw new Error('read-board payload has an invalid "view"');
5623
5668
  }
5624
- return { projectId: payload.projectId, action: payload.action, view: payload.view };
5669
+ if (payload.limit !== void 0 && (typeof payload.limit !== "number" || !Number.isInteger(payload.limit) || payload.limit < 1)) {
5670
+ throw new Error('read-board payload has an invalid "limit"');
5671
+ }
5672
+ if (payload.offset !== void 0 && (typeof payload.offset !== "number" || !Number.isInteger(payload.offset) || payload.offset < 0)) {
5673
+ throw new Error('read-board payload has an invalid "offset"');
5674
+ }
5675
+ return {
5676
+ projectId: payload.projectId,
5677
+ action: payload.action,
5678
+ view: payload.view,
5679
+ limit: payload.limit,
5680
+ offset: payload.offset
5681
+ };
5625
5682
  }
5626
5683
  function parseReadDiffResponsePayload(payload) {
5627
5684
  if (!isRecord(payload)) throw new Error("read-diff response must be an object");
@@ -5942,6 +5999,7 @@ var BOARD_TOOL_READ_NAMES = [
5942
5999
  "list_sessions",
5943
6000
  "get_session_history",
5944
6001
  "get_column_detail",
6002
+ "list_board_profiles",
5945
6003
  "get_handoff_context",
5946
6004
  "get_transcript",
5947
6005
  "get_session_files",
@@ -5955,6 +6013,9 @@ var BOARD_TOOL_WRITE_NAMES = [
5955
6013
  "link_pr",
5956
6014
  "remove_attachment",
5957
6015
  "update_column",
6016
+ "create_board_profile",
6017
+ "update_board_profile",
6018
+ "delete_board_profile",
5958
6019
  "create_backlog_task",
5959
6020
  "promote_backlog",
5960
6021
  "update_backlog_item",
@@ -6122,6 +6183,7 @@ function isBridgeEvent(value) {
6122
6183
  parseReadStreamResponsePayload,
6123
6184
  parseRegisterPushRequestPayload,
6124
6185
  parseSessionEventWire,
6186
+ parseSessionSummaryWire,
6125
6187
  parseSessionUsageWire,
6126
6188
  parseTerminalDimensionsWire,
6127
6189
  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 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") {
@@ -5494,18 +5526,42 @@ function parseTaskCountsByColumnId(value) {
5494
5526
  }
5495
5527
  return counts;
5496
5528
  }
5529
+ function parseSummariesByTaskId(value) {
5530
+ if (value === void 0) return {};
5531
+ if (!isRecord(value)) throw new Error('read-board archived response has a non-object "summariesByTaskId"');
5532
+ const summaries = {};
5533
+ for (const [taskId, summary] of Object.entries(value)) {
5534
+ try {
5535
+ summaries[taskId] = parseSessionSummaryWire(summary);
5536
+ } catch {
5537
+ }
5538
+ }
5539
+ return summaries;
5540
+ }
5497
5541
  function parseReadBoardRequestPayload(payload) {
5498
5542
  if (!isRecord(payload)) throw new Error("read-board payload must be an object");
5499
5543
  if (payload.projectId !== void 0 && typeof payload.projectId !== "string") {
5500
5544
  throw new Error('read-board payload has a non-string "projectId"');
5501
5545
  }
5502
- if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe") {
5546
+ if (payload.action !== void 0 && payload.action !== "subscribe" && payload.action !== "unsubscribe" && payload.action !== "archived") {
5503
5547
  throw new Error('read-board payload has an invalid "action"');
5504
5548
  }
5505
5549
  if (payload.view !== void 0 && payload.view !== "full" && payload.view !== "sessions") {
5506
5550
  throw new Error('read-board payload has an invalid "view"');
5507
5551
  }
5508
- return { projectId: payload.projectId, action: payload.action, view: payload.view };
5552
+ if (payload.limit !== void 0 && (typeof payload.limit !== "number" || !Number.isInteger(payload.limit) || payload.limit < 1)) {
5553
+ throw new Error('read-board payload has an invalid "limit"');
5554
+ }
5555
+ if (payload.offset !== void 0 && (typeof payload.offset !== "number" || !Number.isInteger(payload.offset) || payload.offset < 0)) {
5556
+ throw new Error('read-board payload has an invalid "offset"');
5557
+ }
5558
+ return {
5559
+ projectId: payload.projectId,
5560
+ action: payload.action,
5561
+ view: payload.view,
5562
+ limit: payload.limit,
5563
+ offset: payload.offset
5564
+ };
5509
5565
  }
5510
5566
  function parseReadDiffResponsePayload(payload) {
5511
5567
  if (!isRecord(payload)) throw new Error("read-diff response must be an object");
@@ -5826,6 +5882,7 @@ var BOARD_TOOL_READ_NAMES = [
5826
5882
  "list_sessions",
5827
5883
  "get_session_history",
5828
5884
  "get_column_detail",
5885
+ "list_board_profiles",
5829
5886
  "get_handoff_context",
5830
5887
  "get_transcript",
5831
5888
  "get_session_files",
@@ -5839,6 +5896,9 @@ var BOARD_TOOL_WRITE_NAMES = [
5839
5896
  "link_pr",
5840
5897
  "remove_attachment",
5841
5898
  "update_column",
5899
+ "create_board_profile",
5900
+ "update_board_profile",
5901
+ "delete_board_profile",
5842
5902
  "create_backlog_task",
5843
5903
  "promote_backlog",
5844
5904
  "update_backlog_item",
@@ -6005,6 +6065,7 @@ export {
6005
6065
  parseReadStreamResponsePayload,
6006
6066
  parseRegisterPushRequestPayload,
6007
6067
  parseSessionEventWire,
6068
+ parseSessionSummaryWire,
6008
6069
  parseSessionUsageWire,
6009
6070
  parseTerminalDimensionsWire,
6010
6071
  parseTranscriptEntriesWire,
@@ -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
- /** Defaults to 'subscribe' when omitted. 'unsubscribe' only has an effect when projectId is set - the no-projectId project list is a one-shot read with no live feed to tear down. */
120
- action?: 'subscribe' | 'unsubscribe';
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.
@@ -179,8 +195,33 @@ export interface ReadBoardSnapshotResponsePayload {
179
195
  */
180
196
  taskCountsByColumnId?: Record<string, number>;
181
197
  }
182
- export type ReadBoardResponsePayload = ReadBoardProjectListResponsePayload | ReadBoardSnapshotResponsePayload;
183
- /** Phone-side narrowing of a read-board response (project list or board snapshot). Throws on a malformed required field. */
198
+ /**
199
+ * Returned for `action: 'archived'` - one page of a project's completed tasks
200
+ * (protocol 0.10.0).
201
+ *
202
+ * These never appear in a board snapshot: the desktop builds those from its
203
+ * non-archived task list, so a phone that only ever subscribed has no way to
204
+ * know a completed task exists at all.
205
+ */
206
+ export interface ReadBoardArchivedResponsePayload {
207
+ projectId: string;
208
+ /** Newest-archived first. At most the requested `limit`. */
209
+ archivedTasks: BoardTaskWire[];
210
+ /**
211
+ * Total archived tasks in the project, NOT the length of the page above.
212
+ * Drives the column count and tells the phone whether another page exists.
213
+ */
214
+ archivedTotalCount: number;
215
+ /**
216
+ * Lifetime cost/duration/churn per task id, for the tasks on THIS page that
217
+ * have recorded metrics. Sparse on purpose: a task archived without ever
218
+ * running an agent has nothing to summarize and is simply absent, which is
219
+ * not an error.
220
+ */
221
+ summariesByTaskId: Record<string, SessionSummaryWire>;
222
+ }
223
+ export type ReadBoardResponsePayload = ReadBoardProjectListResponsePayload | ReadBoardSnapshotResponsePayload | ReadBoardArchivedResponsePayload;
224
+ /** Phone-side narrowing of a read-board response (project list, board snapshot, or archived page). Throws on a malformed required field. */
184
225
  export declare function parseReadBoardResponsePayload(payload: JsonValue): ReadBoardResponsePayload;
185
226
  /** 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
227
  export type ReadDiffScope = 'working' | 'staged' | 'branch';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangentic/protocol",
3
- "version": "0.9.0",
3
+ "version": "0.10.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",