@parall/sdk 1.48.0 → 1.49.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/sdk",
3
- "version": "1.48.0",
3
+ "version": "1.49.0",
4
4
  "description": "TypeScript client SDK for Parall — REST + WebSocket client, shared types",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/client.ts CHANGED
@@ -167,6 +167,8 @@ import type {
167
167
  DispatchExpireResult,
168
168
  ResolveRefsResponse,
169
169
  BacklinksResponse,
170
+ OutboundRefsRequest,
171
+ OutboundRefsResponse,
170
172
  RefGraphResponse,
171
173
  BrokenRefsResponse,
172
174
  PushSubscribeRequest,
@@ -223,14 +225,20 @@ import type {
223
225
  BrowserProfileLifecycleRequest,
224
226
  BrowserViewerCommandRequest,
225
227
  BrowserViewerCommandResponse,
228
+ EdgeViewerCommandRequest,
229
+ EdgeViewerCommandResponse,
226
230
  GrantBrowserProfileConsentRequest,
227
231
  EdgeDevice,
228
232
  EdgePlacement,
229
233
  EdgeBrowserProfile,
234
+ EdgeProfileProxyStatus,
235
+ SetEdgeProfileProxyRequest,
230
236
  ClipConnection,
231
237
  EdgeOnboardingStatus,
232
238
  ExecEdgeClipRequest,
233
239
  EdgeClipExecResult,
240
+ ReactionSummary,
241
+ ToggleReactionResponse,
234
242
  } from './types.js';
235
243
 
236
244
  export interface ParallClientOptions {
@@ -421,6 +429,8 @@ export class ParallClient {
421
429
  timeoutMs?: number;
422
430
  signal?: AbortSignal;
423
431
  keepalive?: boolean;
432
+ /** Additional request preconditions such as If-Match. */
433
+ headers?: Record<string, string>;
424
434
  /** Observes the final HTTP status of a successful request (e.g. 200-idempotent-replay vs 201-created). */
425
435
  onStatus?: (status: number) => void;
426
436
  },
@@ -443,7 +453,7 @@ export class ParallClient {
443
453
  if (qs) url += `?${qs}`;
444
454
  }
445
455
 
446
- const headers = this.buildHeaders(path);
456
+ const headers = this.buildHeaders(path, opts?.headers);
447
457
 
448
458
  // Cancellation: the caller's AbortSignal (e.g. a superseded search query)
449
459
  // races the per-request timeout. AbortSignal.any aborts as soon as either
@@ -1095,6 +1105,20 @@ export class ParallClient {
1095
1105
  return this.request('GET', ENDPOINTS.MESSAGE_REPLIES(id), undefined, params);
1096
1106
  }
1097
1107
 
1108
+ // ---- Reactions ----
1109
+
1110
+ async toggleReaction(messageId: string, emoji: string): Promise<ToggleReactionResponse> {
1111
+ return this.request('PUT', ENDPOINTS.MESSAGE_REACTION(messageId, emoji));
1112
+ }
1113
+
1114
+ async listReactions(messageId: string): Promise<ReactionSummary[]> {
1115
+ const res = await this.request<{ reactions: ReactionSummary[] }>(
1116
+ 'GET',
1117
+ ENDPOINTS.MESSAGE_REACTIONS(messageId),
1118
+ );
1119
+ return res.reactions;
1120
+ }
1121
+
1098
1122
  // ---- File Upload ----
1099
1123
 
1100
1124
  async getUploadPresignUrl(orgId: string, req: PresignUploadRequest): Promise<PresignResponse> {
@@ -1757,6 +1781,28 @@ export class ParallClient {
1757
1781
  );
1758
1782
  }
1759
1783
 
1784
+ /**
1785
+ * Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
1786
+ * tab nav for a hosted browser (Cloud Profile). Same request/reply shape as
1787
+ * browserViewerCommand, on the v3 edge pipe; additive — the v2 browser-profile
1788
+ * viewer is unchanged.
1789
+ */
1790
+ async edgeViewerCommand(
1791
+ orgId: string,
1792
+ edgeId: string,
1793
+ req: EdgeViewerCommandRequest,
1794
+ opts?: { timeoutMs?: number; keepalive?: boolean },
1795
+ ): Promise<EdgeViewerCommandResponse> {
1796
+ return this.request(
1797
+ 'POST',
1798
+ ENDPOINTS.ORG_EDGE_VIEWER_COMMAND(orgId, edgeId),
1799
+ req,
1800
+ undefined,
1801
+ false,
1802
+ opts,
1803
+ );
1804
+ }
1805
+
1760
1806
  async resizeMachine(
1761
1807
  orgId: string,
1762
1808
  machineId: string,
@@ -1813,9 +1859,23 @@ export class ParallClient {
1813
1859
 
1814
1860
  // ---- Unread ----
1815
1861
 
1816
- async getUnreadCounts(orgId?: string): Promise<Record<string, UnreadEntry>> {
1862
+ async getUnreadCounts(
1863
+ orgId?: string,
1864
+ opts?: {
1865
+ /** Add unread thread replies (per-thread cursors) to count/mentions.
1866
+ * Opt-in: only clients that can clear thread cursors should pass it. */
1867
+ includeThreadReplies?: boolean;
1868
+ },
1869
+ ): Promise<Record<string, UnreadEntry>> {
1817
1870
  const endpoint = orgId ? ENDPOINTS.ORG_UNREAD(orgId) : ENDPOINTS.UNREAD;
1818
- const res = await this.request<{ data: Record<string, UnreadEntry> }>('GET', endpoint);
1871
+ const res = await this.request<{ data: Record<string, UnreadEntry> }>(
1872
+ 'GET',
1873
+ endpoint,
1874
+ undefined,
1875
+ {
1876
+ include_thread_replies: opts?.includeThreadReplies ? 'true' : undefined,
1877
+ },
1878
+ );
1819
1879
  return res.data;
1820
1880
  }
1821
1881
 
@@ -1823,6 +1883,14 @@ export class ParallClient {
1823
1883
  return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
1824
1884
  }
1825
1885
 
1886
+ /** Mark everything in a chat read: the channel cursor jumps to the latest
1887
+ * top-level message and every thread cursor to its latest reply, in one
1888
+ * idempotent call. The server echoes the same per-cursor WS events the
1889
+ * single-cursor routes emit and auto-clears covered inbox items. */
1890
+ async markAllRead(orgId: string, chatId: string): Promise<void> {
1891
+ return this.request('POST', ENDPOINTS.CHAT_READ_ALL(orgId, chatId));
1892
+ }
1893
+
1826
1894
  async getThreadUnread(
1827
1895
  orgId: string,
1828
1896
  chatId: string,
@@ -3161,6 +3229,17 @@ export class ParallClient {
3161
3229
  return this.request('GET', ENDPOINTS.REFS_BACKLINKS(orgId), undefined, params);
3162
3230
  }
3163
3231
 
3232
+ /**
3233
+ * Outbound prll:// refs authored in a set of sources, as raw ref_links rows
3234
+ * (dedupe/group client-side). Pass `{ thread_root_id }` to list a whole
3235
+ * thread's refs (root + all replies, resolved server-side — the client's
3236
+ * reply window may be partial), or `{ source_type, source_ids }` for
3237
+ * explicit sources (max 500; v1 accepts only message sources).
3238
+ */
3239
+ async listOutboundRefs(orgId: string, req: OutboundRefsRequest): Promise<OutboundRefsResponse> {
3240
+ return this.request('POST', ENDPOINTS.REFS_OUTBOUND(orgId), req);
3241
+ }
3242
+
3164
3243
  /**
3165
3244
  * Bounded multi-hop walk of the prll:// reference graph around `uri`. `uri`
3166
3245
  * must be an entity-level prll:// URI — a refined URI (path/query/fragment) is
@@ -3567,6 +3646,75 @@ export class ParallClient {
3567
3646
  return this.request('GET', ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
3568
3647
  }
3569
3648
 
3649
+ /**
3650
+ * Read a hosted Cloud Profile's egress-proxy status (manager-only: hosted
3651
+ * human maintainer or org admin). Sanitized — the password never comes back.
3652
+ * Typed errors: `EDGE_NOT_HOSTED` (BYOC device), `PROXY_CONFIG_CORRUPT` /
3653
+ * validation codes as 422 when a stored config no longer passes current rules.
3654
+ * `can_mutate` and `lease_status` are the authoritative idle gate; device
3655
+ * list status is not a substitute.
3656
+ */
3657
+ async getEdgeProfileProxy(
3658
+ orgId: string,
3659
+ edgeId: string,
3660
+ profileName: string,
3661
+ ): Promise<EdgeProfileProxyStatus> {
3662
+ return this.request('GET', ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName));
3663
+ }
3664
+
3665
+ /**
3666
+ * Set/replace the profile's egress proxy (full triple every time) — IDLE
3667
+ * ONLY: while the profile's hosted browser is running (a viewer session is
3668
+ * open or a pod is otherwise live) the server answers 409
3669
+ * `EDGE_PROFILE_IN_USE`; close the viewer, wait for idle scale-to-zero, and
3670
+ * retry. The next cold start uses the new egress; browser login state is
3671
+ * preserved across it. Other typed errors: the validation vocabulary
3672
+ * (`INVALID_PROXY_SERVER`, `PROXY_AUTH_INCOMPLETE`,
3673
+ * `PROXY_SERVER_FORBIDDEN_TARGET`, …), `EDGE_DELETING` (409), and
3674
+ * `SECRETBOX_UNCONFIGURED` (503 — server cannot store credentials safely),
3675
+ * and `EDGE_PROFILE_PROXY_STALE` (409 — expectedVersion lost a tab race).
3676
+ */
3677
+ async setEdgeProfileProxy(
3678
+ orgId: string,
3679
+ edgeId: string,
3680
+ profileName: string,
3681
+ req: SetEdgeProfileProxyRequest,
3682
+ expectedVersion?: string,
3683
+ ): Promise<EdgeProfileProxyStatus> {
3684
+ return this.request(
3685
+ 'PUT',
3686
+ ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName),
3687
+ req,
3688
+ undefined,
3689
+ false,
3690
+ {
3691
+ headers: expectedVersion ? { 'If-Match': `"proxy-${expectedVersion}"` } : undefined,
3692
+ },
3693
+ );
3694
+ }
3695
+
3696
+ /**
3697
+ * Clear the profile's egress proxy; the next pod start egresses directly.
3698
+ * Idle-only like set — 409 `EDGE_PROFILE_IN_USE` while the browser is live.
3699
+ */
3700
+ async clearEdgeProfileProxy(
3701
+ orgId: string,
3702
+ edgeId: string,
3703
+ profileName: string,
3704
+ expectedVersion?: string,
3705
+ ): Promise<EdgeProfileProxyStatus> {
3706
+ return this.request(
3707
+ 'DELETE',
3708
+ ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName),
3709
+ undefined,
3710
+ undefined,
3711
+ false,
3712
+ {
3713
+ headers: expectedVersion ? { 'If-Match': `"proxy-${expectedVersion}"` } : undefined,
3714
+ },
3715
+ );
3716
+ }
3717
+
3570
3718
  /**
3571
3719
  * Execute a registry clip command on an Edge device.
3572
3720
  *
@@ -3664,6 +3812,8 @@ function normalizeWikiChangeset(changeset: WikiChangeset): WikiChangeset {
3664
3812
 
3665
3813
  export class ApiError extends Error {
3666
3814
  extras?: Record<string, unknown>;
3815
+ /** Retry-After delta seconds when the server supplies one. */
3816
+ retryAfterSeconds?: number;
3667
3817
  /** Attempted action (authorization denials) — e.g. "chat.add_member". */
3668
3818
  action?: string;
3669
3819
  /** Target resource URI that was evaluated — e.g. "prll://cht_…". */
@@ -3691,7 +3841,7 @@ export class ApiError extends Error {
3691
3841
  * See docs/engineering-design/error-contract-design.md
3692
3842
  */
3693
3843
  function buildApiError(
3694
- res: { status: number; statusText: string },
3844
+ res: { status: number; statusText: string; headers?: { get(name: string): string | null } },
3695
3845
  rawErrorBody: unknown,
3696
3846
  ): ApiError {
3697
3847
  const errorBody =
@@ -3714,6 +3864,8 @@ function buildApiError(
3714
3864
  (typeof errorObj?.code === 'string' ? errorObj.code : undefined) ??
3715
3865
  (typeof errorBody.code === 'string' ? (errorBody.code as string) : undefined);
3716
3866
  const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
3867
+ const retryAfter = Number(res.headers?.get('Retry-After'));
3868
+ if (Number.isFinite(retryAfter) && retryAfter > 0) apiError.retryAfterSeconds = retryAfter;
3717
3869
  // Machine anchors: present under `error`, with a legacy flat fallback.
3718
3870
  const anchors = errorObj ?? errorBody;
3719
3871
  if (typeof anchors.action === 'string') apiError.action = anchors.action;
package/src/constants.ts CHANGED
@@ -410,6 +410,9 @@ export const ENDPOINTS = {
410
410
  MESSAGE_WATCH: (id: string) => `${API_BASE}/messages/${id}/watch`,
411
411
  MESSAGE_WATCHERS: (id: string) => `${API_BASE}/messages/${id}/watchers`,
412
412
  MESSAGE_WATCHING: (id: string) => `${API_BASE}/messages/${id}/watching`,
413
+ MESSAGE_REACTIONS: (id: string) => `${API_BASE}/messages/${id}/reactions`,
414
+ MESSAGE_REACTION: (id: string, emoji: string) =>
415
+ `${API_BASE}/messages/${id}/reactions/${encodeURIComponent(emoji)}`,
413
416
 
414
417
  // Upload (org-scoped)
415
418
  UPLOAD_PRESIGN: (orgId: string) => `${API_BASE}/orgs/${orgId}/upload/presign`,
@@ -780,6 +783,8 @@ export const ENDPOINTS = {
780
783
  UNREAD: `${API_BASE}/me/unread`,
781
784
  ORG_UNREAD: (orgId: string) => `${API_BASE}/orgs/${orgId}/unread`,
782
785
  CHAT_READ: (orgId: string, chatId: string) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
786
+ CHAT_READ_ALL: (orgId: string, chatId: string) =>
787
+ `${API_BASE}/orgs/${orgId}/chats/${chatId}/read-all`,
783
788
  THREAD_UNREAD: (orgId: string, chatId: string, threadRootId: string) =>
784
789
  `${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/unread`,
785
790
  THREAD_READ: (orgId: string, chatId: string, threadRootId: string) =>
@@ -788,6 +793,7 @@ export const ENDPOINTS = {
788
793
  // References (org-scoped)
789
794
  REFS_RESOLVE: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
790
795
  REFS_BACKLINKS: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
796
+ REFS_OUTBOUND: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/outbound`,
791
797
  REFS_GRAPH: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/graph`,
792
798
  REFS_CHECK: (orgId: string) => `${API_BASE}/orgs/${orgId}/refs/check`,
793
799
 
@@ -876,7 +882,15 @@ export const ENDPOINTS = {
876
882
  ORG_EDGE_ONBOARDING: (orgId: string) => `/api/v1/orgs/${orgId}/edge/onboarding`,
877
883
  ORG_EDGE_PROFILES: (orgId: string, edgeId: string) =>
878
884
  `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
885
+ // Per-profile egress proxy (hosted Cloud Profiles; manager-only, human-only).
886
+ ORG_EDGE_PROFILE_PROXY: (orgId: string, edgeId: string, profileName: string) =>
887
+ `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles/${encodeURIComponent(profileName)}/proxy`,
879
888
  ORG_EDGE_EXEC: (orgId: string) => `/api/v1/orgs/${orgId}/edge/exec`,
889
+ // Cloud Edge live viewer command (V1b) — api-server, gated on cap:edge-viewer.
890
+ // Same request/reply shape as the v2 browser-profile viewer, on the v3 edge
891
+ // pipe. Additive: does NOT replace BROWSER_PROFILE_VIEWER_COMMAND.
892
+ ORG_EDGE_VIEWER_COMMAND: (orgId: string, edgeId: string) =>
893
+ `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
880
894
  CLIP_CONNECTIONS: (orgId: string, clipId: string) =>
881
895
  `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
882
896
  CLIP_CONNECTION: (orgId: string, connId: string) =>
@@ -941,6 +955,7 @@ export const WS_EVENTS = {
941
955
  MESSAGE_PATCH: 'message.patch',
942
956
  MESSAGE_EDIT: 'message.edit',
943
957
  MESSAGE_DELETE: 'message.delete',
958
+ MESSAGE_REACTION_UPDATED: 'message.reaction.updated',
944
959
  TYPING_UPDATE: 'typing.update',
945
960
  CHAT_UPDATE: 'chat.update',
946
961
  CHAT_DELETED: 'chat.deleted',
@@ -981,6 +996,7 @@ export const WS_EVENTS = {
981
996
  INBOX_UPDATE: 'inbox.update',
982
997
  INBOX_BULK_UPDATE: 'inbox.bulk_update',
983
998
  READ_POSITION_UPDATED: 'read_position.updated',
999
+ THREAD_READ_POSITION_UPDATED: 'thread_read_position.updated',
984
1000
  DISPATCH_NEW: 'dispatch.new',
985
1001
  DISPATCH_RECEIVED: 'dispatch.received',
986
1002
  DISPATCH_RESOLVED: 'dispatch.resolved',
package/src/types.ts CHANGED
@@ -111,6 +111,12 @@ export interface TransferChatOwnershipRequest {
111
111
  export interface UnreadEntry {
112
112
  count: number;
113
113
  mentions: number;
114
+ /** Share of `count` contributed by unread thread replies (only present when
115
+ * the unread fetch opted into `include_thread_replies`). Top-level timeline
116
+ * unread = count - (thread_count ?? 0). */
117
+ thread_count?: number;
118
+ /** Share of `mentions` contributed by unread thread replies. */
119
+ thread_mentions?: number;
114
120
  since?: string;
115
121
  }
116
122
 
@@ -273,6 +279,12 @@ export interface Message {
273
279
  * absent from the stack. A reload re-derives it from the server's view.
274
280
  */
275
281
  recent_repliers?: User[];
282
+ /**
283
+ * Per-viewer count of thread replies newer than the viewer's thread read
284
+ * cursor, excluding the viewer's own replies (root only). Populated on
285
+ * top-level list responses; omitted when zero.
286
+ */
287
+ unread_reply_count?: number;
276
288
  edited_at: string | null;
277
289
  deleted_at: string | null;
278
290
  created_at: string;
@@ -281,6 +293,27 @@ export interface Message {
281
293
  agent_session_id?: string | null;
282
294
  hints?: MessageHints | null;
283
295
  attachments?: Attachment[];
296
+ reactions?: ReactionSummary[];
297
+ }
298
+
299
+ /**
300
+ * Aggregated reactions for one emoji on a message.
301
+ *
302
+ * `has_reacted` is viewer-relative in REST responses. In
303
+ * `message.reaction.updated` WS broadcasts it is computed for the ACTOR who
304
+ * triggered the toggle — subscribers must recompute their own state from
305
+ * `user_ids` (see docs/engineering-design/ws-protocol.md).
306
+ */
307
+ export interface ReactionSummary {
308
+ emoji: string;
309
+ count: number;
310
+ user_ids: string[];
311
+ has_reacted: boolean;
312
+ }
313
+
314
+ export interface ToggleReactionResponse {
315
+ added: boolean;
316
+ reactions: ReactionSummary[];
284
317
  }
285
318
 
286
319
  export interface Attachment {
@@ -1159,6 +1192,13 @@ export interface Task {
1159
1192
  seq_number: number | null;
1160
1193
  identifier: string | null;
1161
1194
  sort_order: number;
1195
+ /**
1196
+ * Planned start date, YYYY-MM-DD (calendar date, timezone-free) — the
1197
+ * planned schedule's left edge, independent of started_at (when work
1198
+ * actually began). Absent on servers older than migration 145. When both
1199
+ * dates are set, planned_start_date <= due_date.
1200
+ */
1201
+ planned_start_date?: string | null;
1162
1202
  /** Planned completion date, YYYY-MM-DD (calendar date, timezone-free). */
1163
1203
  due_date: string | null;
1164
1204
  assignee?: User;
@@ -1246,6 +1286,8 @@ export interface CreateTaskRequest {
1246
1286
  project_id?: string;
1247
1287
  source_chat_id?: string;
1248
1288
  sort_order?: number;
1289
+ /** Planned start date, YYYY-MM-DD. Must be <= due_date when both are set. */
1290
+ planned_start_date?: string;
1249
1291
  /** Planned completion date, YYYY-MM-DD. */
1250
1292
  due_date?: string;
1251
1293
  }
@@ -1266,6 +1308,12 @@ export interface UpdateTaskRequest {
1266
1308
  * full column. Mutually exclusive with sort_order.
1267
1309
  */
1268
1310
  placement?: 'end';
1311
+ /**
1312
+ * Planned start date, YYYY-MM-DD; explicit null clears it. The post-patch
1313
+ * state must satisfy planned_start_date <= due_date when both are set
1314
+ * (400 INVALID_DATE_RANGE otherwise).
1315
+ */
1316
+ planned_start_date?: string | null;
1269
1317
  /** Planned completion date, YYYY-MM-DD; explicit null clears it. */
1270
1318
  due_date?: string | null;
1271
1319
  /**
@@ -2179,6 +2227,20 @@ export interface MessageDeleteData {
2179
2227
  thread_root_id?: string;
2180
2228
  }
2181
2229
 
2230
+ /**
2231
+ * Payload of the `message.reaction.updated` WS broadcast. `reactions` is the
2232
+ * full aggregated snapshot for the message; its `has_reacted` flags are
2233
+ * actor-relative (see {@link ReactionSummary}) — recompute from `user_ids`.
2234
+ */
2235
+ export interface ReactionUpdatedData {
2236
+ message_id: string;
2237
+ chat_id: string;
2238
+ emoji: string;
2239
+ user_id: string;
2240
+ added: boolean;
2241
+ reactions: ReactionSummary[];
2242
+ }
2243
+
2182
2244
  export interface TypingUpdateData {
2183
2245
  chat_id: string;
2184
2246
  thread_root_id: string | null;
@@ -3073,6 +3135,13 @@ export interface ReadPositionUpdateData {
3073
3135
  last_read_message_id: string;
3074
3136
  }
3075
3137
 
3138
+ /** Per-thread read cursor sync across the user's devices. */
3139
+ export interface ThreadReadPositionUpdateData {
3140
+ chat_id: string;
3141
+ thread_root_id: string;
3142
+ last_read_reply_id: string;
3143
+ }
3144
+
3076
3145
  export interface NotificationAlertData {
3077
3146
  title: string;
3078
3147
  body: string;
@@ -3087,6 +3156,7 @@ export type WsEventMap = {
3087
3156
  'message.patch': MessagePatchData;
3088
3157
  'message.edit': MessageEditData;
3089
3158
  'message.delete': MessageDeleteData;
3159
+ 'message.reaction.updated': ReactionUpdatedData;
3090
3160
  'typing.update': TypingUpdateData;
3091
3161
  'chat.created': ChatCreatedData;
3092
3162
  'chat.update': ChatUpdateData;
@@ -3131,6 +3201,7 @@ export type WsEventMap = {
3131
3201
  'inbox.update': InboxUpdateData;
3132
3202
  'inbox.bulk_update': InboxBulkUpdateData;
3133
3203
  'read_position.updated': ReadPositionUpdateData;
3204
+ 'thread_read_position.updated': ThreadReadPositionUpdateData;
3134
3205
  'dispatch.new': DispatchNewData;
3135
3206
  'dispatch.received': DispatchReceivedData;
3136
3207
  'dispatch.resolved': DispatchResolvedData;
@@ -3335,6 +3406,37 @@ export interface BacklinksResponse {
3335
3406
  next_cursor?: string;
3336
3407
  }
3337
3408
 
3409
+ /**
3410
+ * Request for POST /orgs/{orgId}/refs/outbound — two mutually exclusive
3411
+ * forms, modeled as a union so an invalid mixed shape fails at compile time:
3412
+ * `thread_root_id` resolves a thread's message set server-side (root + ALL
3413
+ * non-deleted replies — the client's reply window may be partial), while
3414
+ * `source_type` + `source_ids` lists explicit sources (max 500; v1 accepts
3415
+ * only `message` sources).
3416
+ */
3417
+ export type OutboundRefsRequest =
3418
+ | { thread_root_id: string; source_type?: never; source_ids?: never }
3419
+ | { source_type: string; source_ids: string[]; thread_root_id?: never };
3420
+
3421
+ /** One raw ref_links row; dedupe/grouping is a client concern. */
3422
+ export interface OutboundRefItem {
3423
+ uri: string;
3424
+ target_type: string;
3425
+ target_id: string;
3426
+ target_path?: string;
3427
+ target_frag?: string;
3428
+ context?: string;
3429
+ source_type: string;
3430
+ source_id: string;
3431
+ position: number;
3432
+ }
3433
+
3434
+ export interface OutboundRefsResponse {
3435
+ data: OutboundRefItem[];
3436
+ /** Set when the server-side sanity cap dropped the tail of the row set. */
3437
+ truncated?: boolean;
3438
+ }
3439
+
3338
3440
  /**
3339
3441
  * One entity in a multi-hop ref-graph traversal (GET /refs/graph). `id`/`uri` are
3340
3442
  * the ref_links endpoint identifier: a bare prll:// entity id for most nodes
@@ -3976,6 +4078,29 @@ export interface BrowserViewerCommandResponse {
3976
4078
  result: Record<string, unknown> | null;
3977
4079
  }
3978
4080
 
4081
+ /**
4082
+ * Cloud Edge live viewer (V1b, design §6). Same request/reply shape as the v2
4083
+ * browser-profile viewer — the transport-agnostic `BrowserViewer` client drives
4084
+ * either — but a distinct type so the edge viewer surface never depends on the
4085
+ * v2 browser-profile viewer symbols.
4086
+ */
4087
+ export interface EdgeViewerCommandRequest {
4088
+ /** Correlates a viewer session. Omit on `stream.start`; the server returns one. */
4089
+ session_id?: string;
4090
+ command: string;
4091
+ input?: Record<string, unknown>;
4092
+ }
4093
+
4094
+ export interface EdgeViewerCommandResponse {
4095
+ session_id: string;
4096
+ /**
4097
+ * Command-specific result. For `stream.start`:
4098
+ * `{ offer_sdp, candidates, ice_servers, streamed_tab_id }`. May be `null` on
4099
+ * the wire (a nil Go result map serializes as JSON null); coalesce to `{}`.
4100
+ */
4101
+ result: Record<string, unknown> | null;
4102
+ }
4103
+
3979
4104
  export interface GrantBrowserProfileConsentRequest {
3980
4105
  clip_id: string;
3981
4106
  }
@@ -4187,6 +4312,45 @@ export interface EdgeBrowserProfile {
4187
4312
  is_default: boolean;
4188
4313
  created_at: string;
4189
4314
  updated_at: string;
4315
+ /**
4316
+ * True when the profile has a fixed egress proxy configured (hosted Cloud
4317
+ * Profiles). Presence flag only — endpoint/username/password are readable
4318
+ * exclusively by managers via the dedicated proxy endpoint, and the password
4319
+ * never leaves the server.
4320
+ */
4321
+ proxy_configured?: boolean;
4322
+ }
4323
+
4324
+ /**
4325
+ * Sanitized per-profile egress proxy status (hosted Cloud Profiles) — the GET
4326
+ * response and the PUT/DELETE result. NEVER carries the password; `password_set`
4327
+ * is the only trace of it.
4328
+ */
4329
+ export interface EdgeProfileProxyStatus {
4330
+ configured: boolean;
4331
+ server?: string;
4332
+ username?: string;
4333
+ password_set: boolean;
4334
+ /** Opaque compare-and-swap token for conditional PUT/DELETE. */
4335
+ version: string;
4336
+ /** Authoritative mutation gate; false whenever a non-released lease exists. */
4337
+ can_mutate: boolean;
4338
+ /** Actual lease state blocking a mutation (never inferred from device status). */
4339
+ lease_status?: 'pending' | 'assigned' | 'active' | 'releasing' | 'repair';
4340
+ /** Suggested delay before re-reading authoritative state. */
4341
+ retry_after_seconds?: number;
4342
+ }
4343
+
4344
+ /**
4345
+ * PUT body for a profile's egress proxy: the FULL triple every time (no
4346
+ * tri-state merge — the stored blob is encrypted, so replace always re-supplies
4347
+ * complete credentials). Clearing is DELETE, never an empty PUT. Username and
4348
+ * password go together or not at all.
4349
+ */
4350
+ export interface SetEdgeProfileProxyRequest {
4351
+ server: string;
4352
+ username?: string;
4353
+ password?: string;
4190
4354
  }
4191
4355
 
4192
4356
  export interface ClipConnection {