@kangentic/protocol 0.12.0 → 0.13.1

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.
@@ -217,17 +217,79 @@ export interface SessionEventWire {
217
217
  toolId?: string;
218
218
  detail?: string;
219
219
  }
220
+ /**
221
+ * The system column roles, mirroring the desktop's `SWIMLANE_ROLES` /
222
+ * `SwimlaneRole` (`src/shared/types.ts`). This package cannot import that
223
+ * file (see the module doc), so the two lists are a deliberate mirror, kept
224
+ * in sync by `tests/unit/protocol/board-column-role-parity.test.ts` on the
225
+ * desktop side rather than by a shared import.
226
+ */
227
+ export declare const BOARD_COLUMN_ROLES: readonly ["todo", "done"];
228
+ export type BoardColumnRoleWire = (typeof BOARD_COLUMN_ROLES)[number];
229
+ /** True for a column whose role is the system 'todo' role. Prefer this at a call site over a literal `=== 'todo'`, which the `(string & {})` widening leaves unchecked. */
230
+ export declare function isTodoRole(role: string | null | undefined): boolean;
231
+ /** True for a column whose role is the system 'done' role. Prefer this at a call site over a literal `=== 'done'`, which the `(string & {})` widening leaves unchecked. */
232
+ export declare function isDoneRole(role: string | null | undefined): boolean;
220
233
  /** Phone-needed subset of the desktop's Swimlane row (snake_case preserved so the desktop mapper is a mechanical pick). */
221
234
  export interface BoardColumnWire {
222
235
  id: string;
223
236
  name: string;
224
237
  description: string | null;
225
- role: string | null;
238
+ /**
239
+ * `'todo'` / `'done'` are the system roles; anything else (including a
240
+ * future role this package predates) is a custom or not-yet-known column,
241
+ * which is exactly what the `(string & {})` escape hatch preserves - the
242
+ * literal union alone would reject a value a newer desktop legitimately
243
+ * sends. That widening does NOT make a typo'd comparison a compile error
244
+ * (a string literal is assignable to `string & {}`), so call sites should
245
+ * use `isTodoRole` / `isDoneRole` rather than `=== 'todo'` / `=== 'done'`.
246
+ */
247
+ role: BoardColumnRoleWire | (string & {}) | null;
226
248
  position: number;
227
249
  color: string;
228
250
  icon: string | null;
229
251
  is_archived: boolean;
230
252
  is_ghost: boolean;
253
+ /**
254
+ * Whether moving a task into this column spawns a successor agent
255
+ * session, derived desktop-side from the column's `auto_spawn` flag and
256
+ * role. This is INTENT, not a guarantee:
257
+ *
258
+ * - `false` - this column does not intend to spawn. Reliable enough to act
259
+ * on ONLY when the column's own `role` is 'todo' or 'done': those two
260
+ * produce a full reset or an archive, and a Board Profile cannot change a
261
+ * column's role, so a client MAY skip a session-swap transition entirely
262
+ * for them. `role` rides this same object, so
263
+ * `isTodoRole(column.role) || isDoneRole(column.role)` is how a client
264
+ * recognizes that case. A `false` that came from `auto_spawn: false` on
265
+ * any other column is intent only, exactly like `true`: a Board Profile
266
+ * can set `autoSpawn: true` for one task on that column, and the move
267
+ * then does spawn.
268
+ * - `true` - this column intends to spawn, but a handful of desktop-side
269
+ * conditions can suspend the old session without a successor actually
270
+ * landing (a board profile disabling auto_spawn for this task, a failed
271
+ * worktree checkout that reverts the move, a shutdown or a newer move
272
+ * racing this one). A client MUST NOT treat `true` as a promise; keep
273
+ * whatever timeout already bounds a session-swap wait and let `true`
274
+ * only skip a redundant one.
275
+ * - `null` / absent - a desktop that predates this field. A client falls
276
+ * back to its pre-existing behavior, the same way an absent
277
+ * `pr_merge_readiness` reads as "never judged".
278
+ *
279
+ * Declared OPTIONAL (`?`) so a hand-built `BoardColumnWire` literal does
280
+ * not have to list it. `parseBoardColumnWire` populates the key from every
281
+ * real wire response regardless, so the optionality costs nothing at
282
+ * runtime.
283
+ *
284
+ * Ordering precondition this field depends on: the phone must learn a
285
+ * MOVE's destination column before the old session's `session-ended`
286
+ * arrives, or it cannot resolve `spawns_session` against the right column.
287
+ * That holds today because `handleTaskMove` emits its board-changed event
288
+ * at the commit point rather than at settle time. See the comment above
289
+ * `emitBoardChanged()` in `src/main/ipc/handlers/task-move.ts`, and
290
+ * `docs/mobile-bridge.md`.
291
+ */
292
+ spawns_session?: boolean | null;
231
293
  }
232
294
  /** Phone-needed subset of the desktop's Task row. A non-null `session_id` is the live-session signal the phone's triage view keys on. */
233
295
  export interface BoardTaskWire {
@@ -244,6 +306,18 @@ export interface BoardTaskWire {
244
306
  pr_number: number | null;
245
307
  pr_url: string | null;
246
308
  pr_state: string | null;
309
+ /**
310
+ * Normalized merge readiness of the linked PR: `ready` / `blocked` /
311
+ * `conflicting` / `queued` / `running` / `unknown`, or null when never
312
+ * judged. `queued` and `running` mean a blocking check is still in flight.
313
+ * Absent from desktops that predate it, which the parser reads as null; a
314
+ * value a client does not know should render as plain open.
315
+ *
316
+ * Declared OPTIONAL (`?`) so a hand-built `BoardTaskWire` literal does not
317
+ * have to list it. `parseBoardTaskWire` populates the key from every real
318
+ * wire response regardless, so the optionality costs nothing at runtime.
319
+ */
320
+ pr_merge_readiness?: string | null;
247
321
  base_branch: string | null;
248
322
  labels: string[];
249
323
  priority: number;
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ 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_COLUMN_ROLES: () => BOARD_COLUMN_ROLES,
23
24
  BOARD_TOOL_READ_NAMES: () => BOARD_TOOL_READ_NAMES,
24
25
  BOARD_TOOL_WRITE_NAMES: () => BOARD_TOOL_WRITE_NAMES,
25
26
  CAPABILITY_VERBS: () => CAPABILITY_VERBS,
@@ -69,11 +70,13 @@ __export(index_exports, {
69
70
  isBoardToolWriteName: () => isBoardToolWriteName,
70
71
  isBridgeEvent: () => isBridgeEvent,
71
72
  isCapabilityVerb: () => isCapabilityVerb,
73
+ isDoneRole: () => isDoneRole,
72
74
  isJsonValue: () => isJsonValue,
73
75
  isPushCategory: () => isPushCategory,
74
76
  isRecord: () => isRecord,
75
77
  isRosterEntryExpired: () => isRosterEntryExpired,
76
78
  isSecureRelayAddress: () => isSecureRelayAddress,
79
+ isTodoRole: () => isTodoRole,
77
80
  openPairingConfirm: () => openPairingConfirm,
78
81
  openPushEnvelope: () => openPushEnvelope,
79
82
  parseActivityEventPayload: () => parseActivityEventPayload,
@@ -5249,6 +5252,15 @@ function openPushEnvelope(pushKey, recipientStaticPublicKey, blob) {
5249
5252
  }
5250
5253
 
5251
5254
  // src/events/payloads.ts
5255
+ var BOARD_COLUMN_ROLES = ["todo", "done"];
5256
+ var TODO_ROLE = "todo";
5257
+ var DONE_ROLE = "done";
5258
+ function isTodoRole(role) {
5259
+ return role === TODO_ROLE;
5260
+ }
5261
+ function isDoneRole(role) {
5262
+ return role === DONE_ROLE;
5263
+ }
5252
5264
  var DIFF_FILE_STATUSES = ["A", "M", "D", "R", "C", "U"];
5253
5265
  var TRANSCRIPT_SYSTEM_SUBTYPES = ["compaction", "command", "command_output", "session_boundary"];
5254
5266
  function requireString(record, field, context) {
@@ -5415,18 +5427,26 @@ function nullableNumber(record, field) {
5415
5427
  const value = record[field];
5416
5428
  return typeof value === "number" ? value : null;
5417
5429
  }
5430
+ function nullableBoolean(record, field) {
5431
+ const value = record[field];
5432
+ return typeof value === "boolean" ? value : null;
5433
+ }
5418
5434
  function parseBoardColumnWire(value) {
5419
5435
  if (!isRecord(value)) throw new Error("board column must be an object");
5420
5436
  return {
5421
5437
  id: requireString(value, "id", "board column"),
5422
5438
  name: requireString(value, "name", "board column"),
5423
5439
  description: nullableString(value, "description"),
5440
+ // An unrecognized role passes through rather than throwing: it is a
5441
+ // custom column (or a future system role this package predates), not a
5442
+ // malformed payload. See the BoardColumnRoleWire doc comment.
5424
5443
  role: nullableString(value, "role"),
5425
5444
  position: requireNumber(value, "position", "board column"),
5426
5445
  color: requireString(value, "color", "board column"),
5427
5446
  icon: nullableString(value, "icon"),
5428
5447
  is_archived: requireBoolean(value, "is_archived", "board column"),
5429
- is_ghost: requireBoolean(value, "is_ghost", "board column")
5448
+ is_ghost: requireBoolean(value, "is_ghost", "board column"),
5449
+ spawns_session: nullableBoolean(value, "spawns_session")
5430
5450
  };
5431
5451
  }
5432
5452
  function parseBoardTaskWire(value) {
@@ -5445,6 +5465,7 @@ function parseBoardTaskWire(value) {
5445
5465
  pr_number: nullableNumber(value, "pr_number"),
5446
5466
  pr_url: nullableString(value, "pr_url"),
5447
5467
  pr_state: nullableString(value, "pr_state"),
5468
+ pr_merge_readiness: nullableString(value, "pr_merge_readiness"),
5448
5469
  base_branch: nullableString(value, "base_branch"),
5449
5470
  labels: Array.isArray(value.labels) ? parseStringArray(value.labels, "board task labels") : [],
5450
5471
  priority: typeof value.priority === "number" ? value.priority : 0,
@@ -6146,6 +6167,7 @@ function isBridgeEvent(value) {
6146
6167
  }
6147
6168
  // Annotate the CommonJS export names for ESM import in node:
6148
6169
  0 && (module.exports = {
6170
+ BOARD_COLUMN_ROLES,
6149
6171
  BOARD_TOOL_READ_NAMES,
6150
6172
  BOARD_TOOL_WRITE_NAMES,
6151
6173
  CAPABILITY_VERBS,
@@ -6195,11 +6217,13 @@ function isBridgeEvent(value) {
6195
6217
  isBoardToolWriteName,
6196
6218
  isBridgeEvent,
6197
6219
  isCapabilityVerb,
6220
+ isDoneRole,
6198
6221
  isJsonValue,
6199
6222
  isPushCategory,
6200
6223
  isRecord,
6201
6224
  isRosterEntryExpired,
6202
6225
  isSecureRelayAddress,
6226
+ isTodoRole,
6203
6227
  openPairingConfirm,
6204
6228
  openPushEnvelope,
6205
6229
  parseActivityEventPayload,
package/dist/index.d.ts CHANGED
@@ -22,5 +22,5 @@ export { formatKeyFingerprint } from './roster/fingerprint';
22
22
  export { CAPABILITY_VERBS, isCapabilityVerb, capabilitySetFromArray, capabilitySetToArray, type CapabilityVerb, type CapabilitySet, } from './capabilities/verbs';
23
23
  export { BOARD_TOOL_READ_NAMES, BOARD_TOOL_WRITE_NAMES, isBoardToolReadName, isBoardToolWriteName, type BoardToolReadName, type BoardToolWriteName, type BoardToolName, } from './capabilities/board-tools';
24
24
  export { isBridgeEvent, parseActivityEventPayload, type BridgeEvent, type TranscriptEvent, type ActivityEvent, type ActivityEventPayload, type TerminalEvent, type TerminalResizeEvent, type BoardEvent, type BoardEventPayload, type DiffEvent, } from './events/event';
25
- export { parseTranscriptEntriesWire, parseTranscriptEventPayload, type TranscriptEventPayload, type TranscriptUpsertWire, isActivityStateWire, isActivityReasonWire, parseSessionUsageWire, parseSessionSummaryWire, parseSessionEventWire, parseTerminalDimensionsWire, parseBoardColumnWire, parseBoardTaskWire, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, 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';
25
+ export { parseTranscriptEntriesWire, parseTranscriptEventPayload, type TranscriptEventPayload, type TranscriptUpsertWire, isActivityStateWire, isActivityReasonWire, parseSessionUsageWire, parseSessionSummaryWire, parseSessionEventWire, parseTerminalDimensionsWire, parseBoardColumnWire, parseBoardTaskWire, parseBacklogItemWire, parseDiffFileListWire, parseDiffFileContentWire, isTodoRole, isDoneRole, BOARD_COLUMN_ROLES, type TranscriptBlockWire, type TranscriptEntryWire, type TranscriptTurnUsageWire, type TranscriptSystemSubtypeWire, type ActivityStateWire, type ActivityReasonWire, type SessionUsageWire, type SessionSummaryWire, type SessionEventWire, type TerminalDimensionsWire, type BoardColumnWire, type BoardColumnRoleWire, type BoardTaskWire, type BacklogItemWire, type DiffFileStatusWire, type DiffFileWire, type DiffFileListWire, type DiffFileContentWire, } from './events/payloads';
26
26
  export type { Transport, TransportState, Unsubscribe } from './transport/transport';
package/dist/index.mjs CHANGED
@@ -5131,6 +5131,15 @@ function openPushEnvelope(pushKey, recipientStaticPublicKey, blob) {
5131
5131
  }
5132
5132
 
5133
5133
  // src/events/payloads.ts
5134
+ var BOARD_COLUMN_ROLES = ["todo", "done"];
5135
+ var TODO_ROLE = "todo";
5136
+ var DONE_ROLE = "done";
5137
+ function isTodoRole(role) {
5138
+ return role === TODO_ROLE;
5139
+ }
5140
+ function isDoneRole(role) {
5141
+ return role === DONE_ROLE;
5142
+ }
5134
5143
  var DIFF_FILE_STATUSES = ["A", "M", "D", "R", "C", "U"];
5135
5144
  var TRANSCRIPT_SYSTEM_SUBTYPES = ["compaction", "command", "command_output", "session_boundary"];
5136
5145
  function requireString(record, field, context) {
@@ -5297,18 +5306,26 @@ function nullableNumber(record, field) {
5297
5306
  const value = record[field];
5298
5307
  return typeof value === "number" ? value : null;
5299
5308
  }
5309
+ function nullableBoolean(record, field) {
5310
+ const value = record[field];
5311
+ return typeof value === "boolean" ? value : null;
5312
+ }
5300
5313
  function parseBoardColumnWire(value) {
5301
5314
  if (!isRecord(value)) throw new Error("board column must be an object");
5302
5315
  return {
5303
5316
  id: requireString(value, "id", "board column"),
5304
5317
  name: requireString(value, "name", "board column"),
5305
5318
  description: nullableString(value, "description"),
5319
+ // An unrecognized role passes through rather than throwing: it is a
5320
+ // custom column (or a future system role this package predates), not a
5321
+ // malformed payload. See the BoardColumnRoleWire doc comment.
5306
5322
  role: nullableString(value, "role"),
5307
5323
  position: requireNumber(value, "position", "board column"),
5308
5324
  color: requireString(value, "color", "board column"),
5309
5325
  icon: nullableString(value, "icon"),
5310
5326
  is_archived: requireBoolean(value, "is_archived", "board column"),
5311
- is_ghost: requireBoolean(value, "is_ghost", "board column")
5327
+ is_ghost: requireBoolean(value, "is_ghost", "board column"),
5328
+ spawns_session: nullableBoolean(value, "spawns_session")
5312
5329
  };
5313
5330
  }
5314
5331
  function parseBoardTaskWire(value) {
@@ -5327,6 +5344,7 @@ function parseBoardTaskWire(value) {
5327
5344
  pr_number: nullableNumber(value, "pr_number"),
5328
5345
  pr_url: nullableString(value, "pr_url"),
5329
5346
  pr_state: nullableString(value, "pr_state"),
5347
+ pr_merge_readiness: nullableString(value, "pr_merge_readiness"),
5330
5348
  base_branch: nullableString(value, "base_branch"),
5331
5349
  labels: Array.isArray(value.labels) ? parseStringArray(value.labels, "board task labels") : [],
5332
5350
  priority: typeof value.priority === "number" ? value.priority : 0,
@@ -6027,6 +6045,7 @@ function isBridgeEvent(value) {
6027
6045
  }
6028
6046
  }
6029
6047
  export {
6048
+ BOARD_COLUMN_ROLES,
6030
6049
  BOARD_TOOL_READ_NAMES,
6031
6050
  BOARD_TOOL_WRITE_NAMES,
6032
6051
  CAPABILITY_VERBS,
@@ -6076,11 +6095,13 @@ export {
6076
6095
  isBoardToolWriteName,
6077
6096
  isBridgeEvent,
6078
6097
  isCapabilityVerb,
6098
+ isDoneRole,
6079
6099
  isJsonValue,
6080
6100
  isPushCategory,
6081
6101
  isRecord,
6082
6102
  isRosterEntryExpired,
6083
6103
  isSecureRelayAddress,
6104
+ isTodoRole,
6084
6105
  openPairingConfirm,
6085
6106
  openPushEnvelope,
6086
6107
  parseActivityEventPayload,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kangentic/protocol",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
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",