@parall/sdk 1.31.0 → 1.32.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.
package/src/client.ts CHANGED
@@ -130,6 +130,7 @@ import type {
130
130
  TransferChatOwnershipRequest,
131
131
  UpdateChatMemberRequest,
132
132
  UnreadEntry,
133
+ ThreadUnreadEntry,
133
134
  BillingSummary,
134
135
  CreditTransaction,
135
136
  CreditTransactionAgentGroup,
@@ -141,6 +142,16 @@ import type {
141
142
  UpdateAutoReloadRequest,
142
143
  ComputePricing,
143
144
  ComputePricingResponse,
145
+ Clip,
146
+ AgentClip,
147
+ CreateClipRequest,
148
+ UpdateClipRequest,
149
+ BindAgentClipRequest,
150
+ InvokeClipRequest,
151
+ InvokeClipResponse,
152
+ OnlineClipInfo,
153
+ RegistryClipInfo,
154
+ MachineClipInstall,
144
155
  } from './types.js';
145
156
 
146
157
  export interface ParallClientOptions {
@@ -163,16 +174,26 @@ export class ParallClient {
163
174
 
164
175
  /** Auth endpoints excluded from automatic 401 refresh to prevent recursion. */
165
176
  private static readonly AUTH_PATHS = new Set([
166
- '/auth/login', '/auth/register', '/auth/refresh', '/auth/logout',
167
- '/auth/verify-email', '/auth/resend-code', '/auth/check-email',
168
- '/auth/forgot-password', '/auth/reset-password',
177
+ '/auth/login',
178
+ '/auth/register',
179
+ '/auth/refresh',
180
+ '/auth/logout',
181
+ '/auth/verify-email',
182
+ '/auth/resend-code',
183
+ '/auth/check-email',
184
+ '/auth/forgot-password',
185
+ '/auth/reset-password',
169
186
  ]);
170
187
 
171
188
  /** Proactive refresh when token expires within this window (seconds). */
172
189
  private static readonly REFRESH_THRESHOLD_S = 5 * 60;
173
190
 
174
191
  private static normalizeFetchError(err: unknown): ApiError {
175
- if (typeof DOMException !== 'undefined' && err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError')) {
192
+ if (
193
+ typeof DOMException !== 'undefined' &&
194
+ err instanceof DOMException &&
195
+ (err.name === 'TimeoutError' || err.name === 'AbortError')
196
+ ) {
176
197
  // The SDK only creates internal timeout signals today. If caller-owned
177
198
  // cancellation is added later, AbortError should get its own code.
178
199
  return new ApiError(0, 'Request timed out', 'REQUEST_TIMEOUT');
@@ -332,14 +353,7 @@ export class ParallClient {
332
353
 
333
354
  if (!res.ok) {
334
355
  const rawErrorBody = await res.json().catch(() => ({}));
335
- const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
336
- const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
337
- const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
338
- const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
339
- const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
340
- const { error: _e, code: _c, message: _m, ...extras } = errorBody;
341
- if (Object.keys(extras).length > 0) apiError.extras = extras;
342
- throw apiError;
356
+ throw buildApiError(res, rawErrorBody);
343
357
  }
344
358
 
345
359
  if (res.status === 204) return undefined as T;
@@ -401,14 +415,7 @@ export class ParallClient {
401
415
 
402
416
  if (!res.ok) {
403
417
  const rawErrorBody = await res.json().catch(() => ({}));
404
- const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
405
- const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
406
- const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
407
- const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
408
- const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
409
- const { error: _e, code: _c, message: _m, ...extras } = errorBody;
410
- if (Object.keys(extras).length > 0) apiError.extras = extras;
411
- throw apiError;
418
+ throw buildApiError(res, rawErrorBody);
412
419
  }
413
420
 
414
421
  if (res.status === 204) return undefined as T;
@@ -454,7 +461,10 @@ export class ParallClient {
454
461
  }
455
462
 
456
463
  async resetPassword(token: string, newPassword: string): Promise<AuthTokens> {
457
- return this.request('POST', ENDPOINTS.AUTH_RESET_PASSWORD, { token, new_password: newPassword });
464
+ return this.request('POST', ENDPOINTS.AUTH_RESET_PASSWORD, {
465
+ token,
466
+ new_password: newPassword,
467
+ });
458
468
  }
459
469
 
460
470
  // ---- WebSocket ----
@@ -487,7 +497,11 @@ export class ParallClient {
487
497
  return this.request('DELETE', ENDPOINTS.USER_AVATAR);
488
498
  }
489
499
 
490
- async uploadAgentAvatar(orgId: string, agentId: string, file: File | Blob): Promise<AvatarUploadResponse> {
500
+ async uploadAgentAvatar(
501
+ orgId: string,
502
+ agentId: string,
503
+ file: File | Blob,
504
+ ): Promise<AvatarUploadResponse> {
491
505
  const fd = new FormData();
492
506
  fd.append('file', file);
493
507
  return this.multipartRequest('POST', ENDPOINTS.AGENT_AVATAR(orgId, agentId), fd);
@@ -501,10 +515,13 @@ export class ParallClient {
501
515
  return this.request('GET', ENDPOINTS.USER(id));
502
516
  }
503
517
 
504
-
505
518
  // ---- Organizations ----
506
519
 
507
- async createOrg(data: { name: string; invite_code: string; avatar_url?: string }): Promise<Organization> {
520
+ async createOrg(data: {
521
+ name: string;
522
+ invite_code: string;
523
+ avatar_url?: string;
524
+ }): Promise<Organization> {
508
525
  return this.request('POST', ENDPOINTS.ORGS, data);
509
526
  }
510
527
 
@@ -524,7 +541,12 @@ export class ParallClient {
524
541
  */
525
542
  async updateOrg(
526
543
  orgId: string,
527
- data: Partial<Pick<Organization, 'name' | 'avatar_url' | 'smart_routing_strategy' | 'smart_routing_agent_id'>>,
544
+ data: Partial<
545
+ Pick<
546
+ Organization,
547
+ 'name' | 'avatar_url' | 'smart_routing_strategy' | 'smart_routing_agent_id'
548
+ >
549
+ >,
528
550
  ): Promise<Organization> {
529
551
  return this.request('PATCH', ENDPOINTS.ORG(orgId), data);
530
552
  }
@@ -539,7 +561,10 @@ export class ParallClient {
539
561
  }
540
562
 
541
563
  async getOnlineMembers(orgId: string): Promise<string[]> {
542
- const res = await this.request<{ user_ids: string[] }>('GET', ENDPOINTS.ORG_MEMBERS_ONLINE(orgId));
564
+ const res = await this.request<{ user_ids: string[] }>(
565
+ 'GET',
566
+ ENDPOINTS.ORG_MEMBERS_ONLINE(orgId),
567
+ );
543
568
  return res.user_ids ?? [];
544
569
  }
545
570
 
@@ -596,12 +621,19 @@ export class ParallClient {
596
621
 
597
622
  // ---- Invitations ----
598
623
 
599
- async createInvitation(orgId: string, email: string, role?: OrgMemberRole): Promise<OrgInvitation> {
624
+ async createInvitation(
625
+ orgId: string,
626
+ email: string,
627
+ role?: OrgMemberRole,
628
+ ): Promise<OrgInvitation> {
600
629
  return this.request('POST', ENDPOINTS.ORG_INVITATIONS(orgId), { email, role });
601
630
  }
602
631
 
603
632
  async getOrgInvitations(orgId: string): Promise<OrgInvitation[]> {
604
- const res = await this.request<{ data: OrgInvitation[] }>('GET', ENDPOINTS.ORG_INVITATIONS(orgId));
633
+ const res = await this.request<{ data: OrgInvitation[] }>(
634
+ 'GET',
635
+ ENDPOINTS.ORG_INVITATIONS(orgId),
636
+ );
605
637
  return res.data;
606
638
  }
607
639
 
@@ -672,16 +704,17 @@ export class ParallClient {
672
704
  jrId: string,
673
705
  decision: 'approve' | 'reject',
674
706
  ): Promise<OrgJoinRequest> {
675
- return this.request(
676
- 'POST',
677
- ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUEST_DECIDE(orgId, jrId),
678
- { decision },
679
- );
707
+ return this.request('POST', ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUEST_DECIDE(orgId, jrId), {
708
+ decision,
709
+ });
680
710
  }
681
711
 
682
712
  // ---- Direct Messages (org-scoped) ----
683
713
 
684
- async sendDirectMessage(orgId: string, req: SendDirectMessageRequest): Promise<DirectMessageResponse> {
714
+ async sendDirectMessage(
715
+ orgId: string,
716
+ req: SendDirectMessageRequest,
717
+ ): Promise<DirectMessageResponse> {
685
718
  return this.request('POST', ENDPOINTS.DM(orgId), req);
686
719
  }
687
720
 
@@ -715,7 +748,22 @@ export class ParallClient {
715
748
  return this.request('GET', ENDPOINTS.CHAT(orgId, chatId));
716
749
  }
717
750
 
718
- async updateChat(orgId: string, chatId: string, data: Partial<Pick<Chat, 'name' | 'avatar_url' | 'description' | 'visibility' | 'agent_routing_mode' | 'smart_routing_strategy' | 'smart_routing_agent_id'>>): Promise<Chat> {
751
+ async updateChat(
752
+ orgId: string,
753
+ chatId: string,
754
+ data: Partial<
755
+ Pick<
756
+ Chat,
757
+ | 'name'
758
+ | 'avatar_url'
759
+ | 'description'
760
+ | 'visibility'
761
+ | 'agent_routing_mode'
762
+ | 'smart_routing_strategy'
763
+ | 'smart_routing_agent_id'
764
+ >
765
+ >,
766
+ ): Promise<Chat> {
719
767
  return this.request('PATCH', ENDPOINTS.CHAT(orgId, chatId), data);
720
768
  }
721
769
 
@@ -737,7 +785,12 @@ export class ParallClient {
737
785
  }
738
786
 
739
787
  async deleteChat(orgId: string, chatId: string, opts?: { force?: boolean }): Promise<void> {
740
- return this.request('DELETE', ENDPOINTS.CHAT(orgId, chatId), undefined, opts?.force ? { force: 'true' } : undefined);
788
+ return this.request(
789
+ 'DELETE',
790
+ ENDPOINTS.CHAT(orgId, chatId),
791
+ undefined,
792
+ opts?.force ? { force: 'true' } : undefined,
793
+ );
741
794
  }
742
795
 
743
796
  async archiveChat(orgId: string, chatId: string): Promise<void> {
@@ -751,7 +804,10 @@ export class ParallClient {
751
804
  // ---- Chat Members ----
752
805
 
753
806
  async getChatMembers(orgId: string, chatId: string): Promise<ChatMember[]> {
754
- const res = await this.request<{ data: ChatMember[] }>('GET', ENDPOINTS.CHAT_MEMBERS(orgId, chatId));
807
+ const res = await this.request<{ data: ChatMember[] }>(
808
+ 'GET',
809
+ ENDPOINTS.CHAT_MEMBERS(orgId, chatId),
810
+ );
755
811
  return res.data;
756
812
  }
757
813
 
@@ -763,15 +819,29 @@ export class ParallClient {
763
819
  return this.request('DELETE', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId));
764
820
  }
765
821
 
766
- async updateChatMemberRole(orgId: string, chatId: string, userId: string, role: ChatMemberRole): Promise<void> {
822
+ async updateChatMemberRole(
823
+ orgId: string,
824
+ chatId: string,
825
+ userId: string,
826
+ role: ChatMemberRole,
827
+ ): Promise<void> {
767
828
  return this.request('PATCH', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId), { role });
768
829
  }
769
830
 
770
- async updateChatMember(orgId: string, chatId: string, userId: string, req: UpdateChatMemberRequest): Promise<ChatMember> {
831
+ async updateChatMember(
832
+ orgId: string,
833
+ chatId: string,
834
+ userId: string,
835
+ req: UpdateChatMemberRequest,
836
+ ): Promise<ChatMember> {
771
837
  return this.request('PATCH', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId), req);
772
838
  }
773
839
 
774
- async transferChatOwnership(orgId: string, chatId: string, req: TransferChatOwnershipRequest): Promise<void> {
840
+ async transferChatOwnership(
841
+ orgId: string,
842
+ chatId: string,
843
+ req: TransferChatOwnershipRequest,
844
+ ): Promise<void> {
775
845
  return this.request('POST', ENDPOINTS.CHAT_TRANSFER_OWNERSHIP(orgId, chatId), req);
776
846
  }
777
847
 
@@ -792,7 +862,12 @@ export class ParallClient {
792
862
  top_level?: boolean;
793
863
  },
794
864
  ): Promise<PaginatedResponse<Message>> {
795
- return this.request('GET', ENDPOINTS.CHAT_MESSAGES(orgId, chatId), undefined, params as Record<string, string | number | boolean | undefined>);
865
+ return this.request(
866
+ 'GET',
867
+ ENDPOINTS.CHAT_MESSAGES(orgId, chatId),
868
+ undefined,
869
+ params as Record<string, string | number | boolean | undefined>,
870
+ );
796
871
  }
797
872
 
798
873
  async getMessage(id: string): Promise<Message> {
@@ -825,7 +900,12 @@ export class ParallClient {
825
900
  }
826
901
 
827
902
  async getFileUrl(id: string, opts?: { download?: boolean }): Promise<FileUrlResponse> {
828
- return this.request('GET', ENDPOINTS.FILE(id), undefined, opts?.download ? { download: true } : undefined);
903
+ return this.request(
904
+ 'GET',
905
+ ENDPOINTS.FILE(id),
906
+ undefined,
907
+ opts?.download ? { download: true } : undefined,
908
+ );
829
909
  }
830
910
 
831
911
  // ---- Approvals ----
@@ -858,7 +938,11 @@ export class ParallClient {
858
938
 
859
939
  // ---- Agents (org-scoped) ----
860
940
 
861
- async createAgent(orgId: string, req: CreateAgentRequest, opts?: { timeoutMs?: number }): Promise<CreateAgentResponse> {
941
+ async createAgent(
942
+ orgId: string,
943
+ req: CreateAgentRequest,
944
+ opts?: { timeoutMs?: number },
945
+ ): Promise<CreateAgentResponse> {
862
946
  return this.request('POST', ENDPOINTS.AGENTS(orgId), req, undefined, false, opts);
863
947
  }
864
948
 
@@ -877,10 +961,7 @@ export class ParallClient {
877
961
  * topology metadata (base URL → provider / tenant) to non-admins,
878
962
  * so this dedicated endpoint is the only read path.
879
963
  */
880
- async getAgentProviderConfig(
881
- orgId: string,
882
- agentId: string,
883
- ): Promise<AgentProviderConfigRead> {
964
+ async getAgentProviderConfig(orgId: string, agentId: string): Promise<AgentProviderConfigRead> {
884
965
  return this.request('GET', ENDPOINTS.AGENT_PROVIDER_CONFIG(orgId, agentId));
885
966
  }
886
967
 
@@ -909,13 +990,25 @@ export class ParallClient {
909
990
  return this.request('DELETE', ENDPOINTS.AGENT_API_KEY(orgId, agentId, key));
910
991
  }
911
992
 
912
- async getAgentActivity(orgId: string, agentId: string, params?: { limit?: number }): Promise<Message[]> {
913
- const res = await this.request<{ data: Message[] }>('GET', ENDPOINTS.AGENT_ACTIVITY(orgId, agentId), undefined, params);
993
+ async getAgentActivity(
994
+ orgId: string,
995
+ agentId: string,
996
+ params?: { limit?: number },
997
+ ): Promise<Message[]> {
998
+ const res = await this.request<{ data: Message[] }>(
999
+ 'GET',
1000
+ ENDPOINTS.AGENT_ACTIVITY(orgId, agentId),
1001
+ undefined,
1002
+ params,
1003
+ );
914
1004
  return res.data;
915
1005
  }
916
1006
 
917
1007
  async getAgentMonitor(orgId: string, agentId: string): Promise<unknown> {
918
- const res = await this.request<{ data: unknown }>('GET', ENDPOINTS.AGENT_MONITOR(orgId, agentId));
1008
+ const res = await this.request<{ data: unknown }>(
1009
+ 'GET',
1010
+ ENDPOINTS.AGENT_MONITOR(orgId, agentId),
1011
+ );
919
1012
  return res.data;
920
1013
  }
921
1014
 
@@ -931,7 +1024,11 @@ export class ParallClient {
931
1024
  return this.request('POST', ENDPOINTS.AGENT_NEW_SESSION(orgId, agentId));
932
1025
  }
933
1026
 
934
- async createAgentSession(orgId: string, agentId: string, req: CreateAgentSessionRequest): Promise<AgentSessionDB> {
1027
+ async createAgentSession(
1028
+ orgId: string,
1029
+ agentId: string,
1030
+ req: CreateAgentSessionRequest,
1031
+ ): Promise<AgentSessionDB> {
935
1032
  return this.request('POST', ENDPOINTS.AGENT_SESSIONS(orgId, agentId), req);
936
1033
  }
937
1034
 
@@ -939,27 +1036,60 @@ export class ParallClient {
939
1036
  * List agent sessions.
940
1037
  * @param params.status - Comma-separated status filter (e.g., `'open'`).
941
1038
  */
942
- async getAgentSessions(orgId: string, agentId: string, params?: { limit?: number; status?: string; cursor?: string }): Promise<PaginatedResponse<AgentSessionDB>> {
1039
+ async getAgentSessions(
1040
+ orgId: string,
1041
+ agentId: string,
1042
+ params?: { limit?: number; status?: string; cursor?: string },
1043
+ ): Promise<PaginatedResponse<AgentSessionDB>> {
943
1044
  return this.request('GET', ENDPOINTS.AGENT_SESSIONS(orgId, agentId), undefined, params);
944
1045
  }
945
1046
 
946
- async getAgentSession(orgId: string, agentId: string, sessionId: string): Promise<AgentSessionDB> {
1047
+ async getAgentSession(
1048
+ orgId: string,
1049
+ agentId: string,
1050
+ sessionId: string,
1051
+ ): Promise<AgentSessionDB> {
947
1052
  return this.request('GET', ENDPOINTS.AGENT_SESSION(orgId, agentId, sessionId));
948
1053
  }
949
1054
 
950
- async updateAgentSession(orgId: string, agentId: string, sessionId: string, req: UpdateAgentSessionRequest): Promise<AgentSessionDB> {
1055
+ async updateAgentSession(
1056
+ orgId: string,
1057
+ agentId: string,
1058
+ sessionId: string,
1059
+ req: UpdateAgentSessionRequest,
1060
+ ): Promise<AgentSessionDB> {
951
1061
  return this.request('PATCH', ENDPOINTS.AGENT_SESSION(orgId, agentId, sessionId), req);
952
1062
  }
953
1063
 
954
- async createAgentStep(orgId: string, agentId: string, sessionId: string, req: CreateAgentStepRequest): Promise<AgentStep> {
1064
+ async createAgentStep(
1065
+ orgId: string,
1066
+ agentId: string,
1067
+ sessionId: string,
1068
+ req: CreateAgentStepRequest,
1069
+ ): Promise<AgentStep> {
955
1070
  return this.request('POST', ENDPOINTS.AGENT_SESSION_STEPS(orgId, agentId, sessionId), req);
956
1071
  }
957
1072
 
958
- async getAgentSessionSteps(orgId: string, agentId: string, sessionId: string, params?: { limit?: number; cursor?: string }): Promise<PaginatedResponse<AgentStep>> {
959
- return this.request('GET', ENDPOINTS.AGENT_SESSION_STEPS(orgId, agentId, sessionId), undefined, params);
1073
+ async getAgentSessionSteps(
1074
+ orgId: string,
1075
+ agentId: string,
1076
+ sessionId: string,
1077
+ params?: { limit?: number; cursor?: string },
1078
+ ): Promise<PaginatedResponse<AgentStep>> {
1079
+ return this.request(
1080
+ 'GET',
1081
+ ENDPOINTS.AGENT_SESSION_STEPS(orgId, agentId, sessionId),
1082
+ undefined,
1083
+ params,
1084
+ );
960
1085
  }
961
1086
 
962
- async getAgentSessionStep(orgId: string, agentId: string, sessionId: string, stepId: string): Promise<AgentStep> {
1087
+ async getAgentSessionStep(
1088
+ orgId: string,
1089
+ agentId: string,
1090
+ sessionId: string,
1091
+ stepId: string,
1092
+ ): Promise<AgentStep> {
963
1093
  return this.request('GET', ENDPOINTS.AGENT_SESSION_STEP(orgId, agentId, sessionId, stepId));
964
1094
  }
965
1095
 
@@ -978,11 +1108,7 @@ export class ParallClient {
978
1108
  agentId: string,
979
1109
  req: StartRuntimeAuthSessionRequest = {},
980
1110
  ): Promise<RuntimeAuthSession> {
981
- return this.request(
982
- 'POST',
983
- ENDPOINTS.AGENT_RUNTIME_AUTH_SESSIONS(orgId, agentId),
984
- req,
985
- );
1111
+ return this.request('POST', ENDPOINTS.AGENT_RUNTIME_AUTH_SESSIONS(orgId, agentId), req);
986
1112
  }
987
1113
 
988
1114
  async completeAgentRuntimeAuthSession(
@@ -1042,7 +1168,12 @@ export class ParallClient {
1042
1168
  do {
1043
1169
  const params: Record<string, string> = { limit: '100' };
1044
1170
  if (cursor) params.cursor = cursor;
1045
- const res = await this.request<PaginatedResponse<Task>>('GET', ENDPOINTS.AGENT_TASKS(orgId, agentId), undefined, params);
1171
+ const res = await this.request<PaginatedResponse<Task>>(
1172
+ 'GET',
1173
+ ENDPOINTS.AGENT_TASKS(orgId, agentId),
1174
+ undefined,
1175
+ params,
1176
+ );
1046
1177
  all.push(...res.data);
1047
1178
  cursor = res.has_more ? res.next_cursor : undefined;
1048
1179
  } while (cursor);
@@ -1080,11 +1211,17 @@ export class ParallClient {
1080
1211
  return this.request('GET', ENDPOINTS.MACHINE_LOGS(orgId, machineId), undefined, { lines });
1081
1212
  }
1082
1213
 
1083
- async restartMachine(orgId: string, machineId: string): Promise<{ machine_id: string; command_id: string }> {
1214
+ async restartMachine(
1215
+ orgId: string,
1216
+ machineId: string,
1217
+ ): Promise<{ machine_id: string; command_id: string }> {
1084
1218
  return this.request('POST', ENDPOINTS.MACHINE_RESTART(orgId, machineId));
1085
1219
  }
1086
1220
 
1087
- async restartAllMachines(orgId: string): Promise<{ data: Array<{ machine_id: string; command_id?: string; status: string; error?: string }>; total: number }> {
1221
+ async restartAllMachines(orgId: string): Promise<{
1222
+ data: Array<{ machine_id: string; command_id?: string; status: string; error?: string }>;
1223
+ total: number;
1224
+ }> {
1088
1225
  return this.request('POST', ENDPOINTS.MACHINE_RESTART_ALL(orgId));
1089
1226
  }
1090
1227
 
@@ -1094,7 +1231,10 @@ export class ParallClient {
1094
1231
  * Create a new self-hosted daemon-mode Machine. Returns the Machine row +
1095
1232
  * one-shot mck_ token.
1096
1233
  */
1097
- async createMachine(orgId: string, opts: { label?: string; compute_mode: 'local' }): Promise<{
1234
+ async createMachine(
1235
+ orgId: string,
1236
+ opts: { label?: string; compute_mode: 'local'; llm_source?: 'parall' | 'runtime_auth' },
1237
+ ): Promise<{
1098
1238
  machine: Machine;
1099
1239
  machine_key: string;
1100
1240
  key_id: string;
@@ -1109,11 +1249,18 @@ export class ParallClient {
1109
1249
  */
1110
1250
  async getDaemonMachines(orgId: string): Promise<Machine[]> {
1111
1251
  const all = await this.getMachines(orgId);
1112
- return all.filter((m) => m.daemon_mode && m.compute_mode === 'local' && m.status !== 'terminated');
1252
+ return all.filter(
1253
+ (m) => m.daemon_mode && m.compute_mode === 'local' && m.status !== 'terminated',
1254
+ );
1113
1255
  }
1114
1256
 
1115
1257
  /** Attach an agent to a daemon-mode Machine. */
1116
- async attachAgent(orgId: string, machineId: string, agentId: string, opts?: AttachAgentRequest): Promise<{ status: string; machine_id: string; agent_id: string }> {
1258
+ async attachAgent(
1259
+ orgId: string,
1260
+ machineId: string,
1261
+ agentId: string,
1262
+ opts?: AttachAgentRequest,
1263
+ ): Promise<{ status: string; machine_id: string; agent_id: string }> {
1117
1264
  return this.request('POST', ENDPOINTS.MACHINE_ATTACH_AGENT(orgId, machineId, agentId), opts);
1118
1265
  }
1119
1266
 
@@ -1123,20 +1270,36 @@ export class ParallClient {
1123
1270
  }
1124
1271
 
1125
1272
  async listAgentWorkspaceStates(orgId: string, machineId: string): Promise<AgentWorkspaceState[]> {
1126
- const res = await this.request<{ data: AgentWorkspaceState[] }>('GET', ENDPOINTS.MACHINE_WORKSPACE_STATES(orgId, machineId));
1273
+ const res = await this.request<{ data: AgentWorkspaceState[] }>(
1274
+ 'GET',
1275
+ ENDPOINTS.MACHINE_WORKSPACE_STATES(orgId, machineId),
1276
+ );
1127
1277
  return res.data;
1128
1278
  }
1129
1279
 
1130
- async patchAgentDaemonConfig(orgId: string, machineId: string, agentId: string, daemonConfig: DaemonAgentConfig): Promise<void> {
1131
- return this.request('PATCH', ENDPOINTS.MACHINE_AGENT_DAEMON_CONFIG(orgId, machineId, agentId), { daemon_config: daemonConfig });
1280
+ async patchAgentDaemonConfig(
1281
+ orgId: string,
1282
+ machineId: string,
1283
+ agentId: string,
1284
+ daemonConfig: DaemonAgentConfig,
1285
+ ): Promise<void> {
1286
+ return this.request('PATCH', ENDPOINTS.MACHINE_AGENT_DAEMON_CONFIG(orgId, machineId, agentId), {
1287
+ daemon_config: daemonConfig,
1288
+ });
1132
1289
  }
1133
1290
 
1134
1291
  async retryAgentWorkspaceSetup(orgId: string, machineId: string, agentId: string): Promise<void> {
1135
1292
  return this.request('POST', ENDPOINTS.MACHINE_AGENT_WORKSPACE_SETUP(orgId, machineId, agentId));
1136
1293
  }
1137
1294
 
1138
- async patchMachineLLMSource(orgId: string, machineId: string, llmSource: string): Promise<Machine> {
1139
- return this.request('PATCH', ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), { llm_source: llmSource });
1295
+ async patchMachineLLMSource(
1296
+ orgId: string,
1297
+ machineId: string,
1298
+ llmSource: string,
1299
+ ): Promise<Machine> {
1300
+ return this.request('PATCH', ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), {
1301
+ llm_source: llmSource,
1302
+ });
1140
1303
  }
1141
1304
 
1142
1305
  /** Get machine-level runtime auth state. */
@@ -1158,7 +1321,11 @@ export class ParallClient {
1158
1321
  sessionId: string,
1159
1322
  req: CompleteRuntimeAuthSessionRequest,
1160
1323
  ): Promise<RuntimeAuthSession> {
1161
- return this.request('POST', ENDPOINTS.MACHINE_RUNTIME_AUTH_SESSION_COMPLETE(orgId, machineId, sessionId), req);
1324
+ return this.request(
1325
+ 'POST',
1326
+ ENDPOINTS.MACHINE_RUNTIME_AUTH_SESSION_COMPLETE(orgId, machineId, sessionId),
1327
+ req,
1328
+ );
1162
1329
  }
1163
1330
 
1164
1331
  async disconnectMachineRuntimeAuth(orgId: string, machineId: string): Promise<void> {
@@ -1188,6 +1355,25 @@ export class ParallClient {
1188
1355
  return res.data;
1189
1356
  }
1190
1357
 
1358
+ /** `GET /machines/me/clip-installs` — registry clips this machine should
1359
+ * install but hasn't yet (durable reconcile, mck_-authed). */
1360
+ async listClipInstalls(): Promise<MachineClipInstall[]> {
1361
+ const res = await this.request<{ data: MachineClipInstall[] }>(
1362
+ 'GET',
1363
+ ENDPOINTS.MACHINES_ME_CLIP_INSTALLS,
1364
+ );
1365
+ return res.data;
1366
+ }
1367
+
1368
+ /** `POST /machines/me/clip-installs/{clipId}/installed` — report a finished install. */
1369
+ async reportClipInstall(clipId: string, version?: string): Promise<void> {
1370
+ return this.request(
1371
+ 'POST',
1372
+ ENDPOINTS.MACHINES_ME_CLIP_INSTALL_DONE(clipId),
1373
+ version ? { version } : undefined,
1374
+ );
1375
+ }
1376
+
1191
1377
  /**
1192
1378
  * `POST /machines/me/health` — bump the Machine's `updated_at` to now.
1193
1379
  * Body is intentionally empty; the server ignores any payload. The
@@ -1199,13 +1385,20 @@ export class ParallClient {
1199
1385
  return this.request('POST', ENDPOINTS.MACHINES_ME_HEALTH, body);
1200
1386
  }
1201
1387
 
1202
- async reportAgentWorkspaceState(agentId: string, state: {
1203
- config_hash: string;
1204
- status: AgentWorkspaceReportStatus;
1205
- last_error?: string | null;
1206
- output_tail?: string | null;
1207
- }): Promise<void> {
1208
- const res = await this.request<{ status?: string }>('PUT', ENDPOINTS.MACHINES_ME_AGENT_WORKSPACE_STATE(agentId), state);
1388
+ async reportAgentWorkspaceState(
1389
+ agentId: string,
1390
+ state: {
1391
+ config_hash: string;
1392
+ status: AgentWorkspaceReportStatus;
1393
+ last_error?: string | null;
1394
+ output_tail?: string | null;
1395
+ },
1396
+ ): Promise<void> {
1397
+ const res = await this.request<{ status?: string }>(
1398
+ 'PUT',
1399
+ ENDPOINTS.MACHINES_ME_AGENT_WORKSPACE_STATE(agentId),
1400
+ state,
1401
+ );
1209
1402
  if (res?.status === 'ignored_stale') {
1210
1403
  throw new ApiError(409, 'Workspace state report ignored as stale', 'STALE_WORKSPACE_STATE');
1211
1404
  }
@@ -1227,11 +1420,18 @@ export class ParallClient {
1227
1420
  return this.request('POST', ENDPOINTS.MACHINES_ME_WS_TICKET);
1228
1421
  }
1229
1422
 
1230
- async postBrowseResponse(requestId: string, response: { request_id: string; path: string; entries: FilesystemEntry[]; error?: string }): Promise<void> {
1423
+ async postBrowseResponse(
1424
+ requestId: string,
1425
+ response: { request_id: string; path: string; entries: FilesystemEntry[]; error?: string },
1426
+ ): Promise<void> {
1231
1427
  return this.request('POST', ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
1232
1428
  }
1233
1429
 
1234
- async resizeMachine(orgId: string, machineId: string, spec: ResizeMachineRequest): Promise<Machine> {
1430
+ async resizeMachine(
1431
+ orgId: string,
1432
+ machineId: string,
1433
+ spec: ResizeMachineRequest,
1434
+ ): Promise<Machine> {
1235
1435
  return this.request('PATCH', ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
1236
1436
  }
1237
1437
 
@@ -1240,13 +1440,32 @@ export class ParallClient {
1240
1440
  await this.request('POST', ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
1241
1441
  }
1242
1442
 
1243
- async browseMachineFilesystem(orgId: string, machineId: string, path: string): Promise<BrowseMachineFilesystemResponse> {
1244
- return this.request('POST', ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path }, undefined, false, { timeoutMs: 15_000 });
1443
+ async browseMachineFilesystem(
1444
+ orgId: string,
1445
+ machineId: string,
1446
+ path: string,
1447
+ ): Promise<BrowseMachineFilesystemResponse> {
1448
+ return this.request(
1449
+ 'POST',
1450
+ ENDPOINTS.MACHINE_BROWSE(orgId, machineId),
1451
+ { path },
1452
+ undefined,
1453
+ false,
1454
+ { timeoutMs: 15_000 },
1455
+ );
1245
1456
  }
1246
1457
 
1247
1458
  /** Create a new machine key. Returns the raw key string (shown once) + metadata. */
1248
- async createMachineKey(orgId: string, machineId: string, name?: string): Promise<{ machine_key: string; key_id: string; key: MachineKey }> {
1249
- return this.request('POST', ENDPOINTS.MACHINE_KEYS(orgId, machineId), name ? { name } : undefined);
1459
+ async createMachineKey(
1460
+ orgId: string,
1461
+ machineId: string,
1462
+ name?: string,
1463
+ ): Promise<{ machine_key: string; key_id: string; key: MachineKey }> {
1464
+ return this.request(
1465
+ 'POST',
1466
+ ENDPOINTS.MACHINE_KEYS(orgId, machineId),
1467
+ name ? { name } : undefined,
1468
+ );
1250
1469
  }
1251
1470
 
1252
1471
  /** Revoke (soft-delete) a machine key by ID. */
@@ -1268,12 +1487,25 @@ export class ParallClient {
1268
1487
  return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
1269
1488
  }
1270
1489
 
1490
+ async getThreadUnread(
1491
+ orgId: string,
1492
+ chatId: string,
1493
+ threadRootId: string,
1494
+ ): Promise<ThreadUnreadEntry> {
1495
+ return this.request('GET', ENDPOINTS.THREAD_UNREAD(orgId, chatId, threadRootId));
1496
+ }
1497
+
1271
1498
  // ---- Inbox ----
1272
1499
 
1273
- async getInbox(orgId: string, params?: {
1274
- type?: string; read?: string;
1275
- limit?: number; cursor?: string;
1276
- }): Promise<PaginatedResponse<InboxItem>> {
1500
+ async getInbox(
1501
+ orgId: string,
1502
+ params?: {
1503
+ type?: string;
1504
+ read?: string;
1505
+ limit?: number;
1506
+ cursor?: string;
1507
+ },
1508
+ ): Promise<PaginatedResponse<InboxItem>> {
1277
1509
  return this.request('GET', ENDPOINTS.INBOX(orgId), undefined, params as Record<string, string>);
1278
1510
  }
1279
1511
 
@@ -1314,17 +1546,36 @@ export class ParallClient {
1314
1546
 
1315
1547
  // ---- Dispatch (agent event delivery) ----
1316
1548
 
1317
- async getDispatch(orgId: string, params?: {
1318
- limit?: number; cursor?: string;
1319
- }): Promise<PaginatedResponse<DispatchEvent>> {
1320
- return this.request('GET', ENDPOINTS.DISPATCH(orgId), undefined, params as Record<string, string>);
1549
+ async getDispatch(
1550
+ orgId: string,
1551
+ params?: {
1552
+ limit?: number;
1553
+ cursor?: string;
1554
+ },
1555
+ ): Promise<PaginatedResponse<DispatchEvent>> {
1556
+ return this.request(
1557
+ 'GET',
1558
+ ENDPOINTS.DISPATCH(orgId),
1559
+ undefined,
1560
+ params as Record<string, string>,
1561
+ );
1321
1562
  }
1322
1563
 
1323
1564
  async getDispatchPendingCount(orgId: string): Promise<{ count: number }> {
1324
1565
  return this.request('GET', ENDPOINTS.DISPATCH_PENDING_COUNT(orgId));
1325
1566
  }
1326
1567
 
1327
- async ackDispatch(orgId: string, source: { source_type: string; source_id: string }): Promise<void> {
1568
+ async markDispatchReceived(
1569
+ orgId: string,
1570
+ source: { source_type: string; source_id: string },
1571
+ ): Promise<void> {
1572
+ return this.request('POST', ENDPOINTS.DISPATCH_RECEIVED(orgId), source);
1573
+ }
1574
+
1575
+ async ackDispatch(
1576
+ orgId: string,
1577
+ source: { source_type: string; source_id: string },
1578
+ ): Promise<void> {
1328
1579
  return this.request('POST', ENDPOINTS.DISPATCH_ACK(orgId), source);
1329
1580
  }
1330
1581
 
@@ -1332,6 +1583,20 @@ export class ParallClient {
1332
1583
  return this.request('POST', ENDPOINTS.DISPATCH_ACK_BY_ID(orgId, id));
1333
1584
  }
1334
1585
 
1586
+ async getDispatchByMessages(
1587
+ orgId: string,
1588
+ chatId: string,
1589
+ messageIds: string[],
1590
+ ): Promise<{ data: DispatchEvent[] }> {
1591
+ const params = { chat_id: chatId, message_ids: messageIds.join(',') };
1592
+ return this.request(
1593
+ 'GET',
1594
+ ENDPOINTS.DISPATCH_BY_MESSAGES(orgId),
1595
+ undefined,
1596
+ params as unknown as Record<string, string>,
1597
+ );
1598
+ }
1599
+
1335
1600
  // ---- Platform Config ----
1336
1601
 
1337
1602
  /**
@@ -1362,14 +1627,7 @@ export class ParallClient {
1362
1627
 
1363
1628
  if (!res.ok) {
1364
1629
  const rawErrorBody = await res.json().catch(() => ({}));
1365
- const errorBody = rawErrorBody !== null && typeof rawErrorBody === 'object' ? rawErrorBody as Record<string, unknown> : {};
1366
- const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
1367
- const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
1368
- const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
1369
- const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
1370
- const { error: _e, code: _c, message: _m, ...extras } = errorBody;
1371
- if (Object.keys(extras).length > 0) apiError.extras = extras;
1372
- throw apiError;
1630
+ throw buildApiError(res, rawErrorBody);
1373
1631
  }
1374
1632
 
1375
1633
  return res.json() as Promise<PlatformConfigResponse>;
@@ -1381,18 +1639,22 @@ export class ParallClient {
1381
1639
  return this.request('POST', ENDPOINTS.TASKS(orgId), data);
1382
1640
  }
1383
1641
 
1384
- async getTasks(orgId: string, params?: {
1385
- status?: string;
1386
- priority?: string;
1387
- assignee_id?: string;
1388
- parent_id?: string;
1389
- project_id?: string;
1390
- limit?: number;
1391
- cursor?: string;
1392
- sort?: string;
1393
- order?: string;
1394
- scope?: 'active' | 'archived' | 'all';
1395
- }): Promise<PaginatedResponse<Task>> {
1642
+ async getTasks(
1643
+ orgId: string,
1644
+ params?: {
1645
+ q?: string;
1646
+ status?: string;
1647
+ priority?: string;
1648
+ assignee_id?: string;
1649
+ parent_id?: string;
1650
+ project_id?: string;
1651
+ limit?: number;
1652
+ cursor?: string;
1653
+ sort?: string;
1654
+ order?: string;
1655
+ scope?: 'active' | 'archived' | 'all';
1656
+ },
1657
+ ): Promise<PaginatedResponse<Task>> {
1396
1658
  return this.request('GET', ENDPOINTS.TASKS(orgId), undefined, params);
1397
1659
  }
1398
1660
 
@@ -1405,7 +1667,12 @@ export class ParallClient {
1405
1667
  }
1406
1668
 
1407
1669
  async deleteTask(orgId: string, taskId: string, opts?: { force?: boolean }): Promise<void> {
1408
- return this.request('DELETE', ENDPOINTS.TASK(orgId, taskId), undefined, opts?.force ? { force: 'true' } : undefined);
1670
+ return this.request(
1671
+ 'DELETE',
1672
+ ENDPOINTS.TASK(orgId, taskId),
1673
+ undefined,
1674
+ opts?.force ? { force: 'true' } : undefined,
1675
+ );
1409
1676
  }
1410
1677
 
1411
1678
  async archiveTask(orgId: string, taskId: string): Promise<void> {
@@ -1425,10 +1692,21 @@ export class ParallClient {
1425
1692
  }
1426
1693
 
1427
1694
  async getTaskWatchers(orgId: string, taskId: string): Promise<TaskWatcher[]> {
1428
- const res = await this.request<{ data: TaskWatcher[] }>('GET', ENDPOINTS.TASK_WATCHERS(orgId, taskId));
1695
+ const res = await this.request<{ data: TaskWatcher[] }>(
1696
+ 'GET',
1697
+ ENDPOINTS.TASK_WATCHERS(orgId, taskId),
1698
+ );
1429
1699
  return res.data;
1430
1700
  }
1431
1701
 
1702
+ async subscribeTaskMember(orgId: string, taskId: string, userId: string): Promise<void> {
1703
+ return this.request('PUT', ENDPOINTS.TASK_SUBSCRIBER(orgId, taskId, userId));
1704
+ }
1705
+
1706
+ async unsubscribeTaskMember(orgId: string, taskId: string, userId: string): Promise<void> {
1707
+ return this.request('DELETE', ENDPOINTS.TASK_SUBSCRIBER(orgId, taskId, userId));
1708
+ }
1709
+
1432
1710
  async watchThread(threadRootId: string): Promise<void> {
1433
1711
  return this.request('POST', ENDPOINTS.MESSAGE_WATCH(threadRootId));
1434
1712
  }
@@ -1438,26 +1716,42 @@ export class ParallClient {
1438
1716
  }
1439
1717
 
1440
1718
  async getThreadWatchers(threadRootId: string): Promise<ThreadWatcher[]> {
1441
- const res = await this.request<{ data: ThreadWatcher[] }>('GET', ENDPOINTS.MESSAGE_WATCHERS(threadRootId));
1719
+ const res = await this.request<{ data: ThreadWatcher[] }>(
1720
+ 'GET',
1721
+ ENDPOINTS.MESSAGE_WATCHERS(threadRootId),
1722
+ );
1442
1723
  return res.data;
1443
1724
  }
1444
1725
 
1445
1726
  async isWatchingThread(threadRootId: string): Promise<boolean> {
1446
- const res = await this.request<{ watching: boolean }>('GET', ENDPOINTS.MESSAGE_WATCHING(threadRootId));
1727
+ const res = await this.request<{ watching: boolean }>(
1728
+ 'GET',
1729
+ ENDPOINTS.MESSAGE_WATCHING(threadRootId),
1730
+ );
1447
1731
  return res.watching;
1448
1732
  }
1449
1733
 
1450
1734
  async getSubtasks(orgId: string, taskId: string): Promise<Task[]> {
1451
- const res = await this.request<PaginatedResponse<Task>>('GET', ENDPOINTS.TASK_SUBTASKS(orgId, taskId));
1735
+ const res = await this.request<PaginatedResponse<Task>>(
1736
+ 'GET',
1737
+ ENDPOINTS.TASK_SUBTASKS(orgId, taskId),
1738
+ );
1452
1739
  return res.data;
1453
1740
  }
1454
1741
 
1455
- async createTaskRelation(orgId: string, taskId: string, data: CreateTaskRelationRequest): Promise<TaskRelation> {
1742
+ async createTaskRelation(
1743
+ orgId: string,
1744
+ taskId: string,
1745
+ data: CreateTaskRelationRequest,
1746
+ ): Promise<TaskRelation> {
1456
1747
  return this.request('POST', ENDPOINTS.TASK_RELATIONS(orgId, taskId), data);
1457
1748
  }
1458
1749
 
1459
1750
  async getTaskRelations(orgId: string, taskId: string): Promise<TaskRelation[]> {
1460
- const res = await this.request<{ data: TaskRelation[] }>('GET', ENDPOINTS.TASK_RELATIONS(orgId, taskId));
1751
+ const res = await this.request<{ data: TaskRelation[] }>(
1752
+ 'GET',
1753
+ ENDPOINTS.TASK_RELATIONS(orgId, taskId),
1754
+ );
1461
1755
  return res.data;
1462
1756
  }
1463
1757
 
@@ -1482,9 +1776,15 @@ export class ParallClient {
1482
1776
 
1483
1777
  // ---- Task Comments ----
1484
1778
 
1485
- async getTaskComments(orgId: string, taskId: string, params?: {
1486
- limit?: number; cursor?: string; order?: string;
1487
- }): Promise<PaginatedResponse<TaskComment>> {
1779
+ async getTaskComments(
1780
+ orgId: string,
1781
+ taskId: string,
1782
+ params?: {
1783
+ limit?: number;
1784
+ cursor?: string;
1785
+ order?: string;
1786
+ },
1787
+ ): Promise<PaginatedResponse<TaskComment>> {
1488
1788
  return this.request('GET', ENDPOINTS.TASK_COMMENTS(orgId, taskId), undefined, params);
1489
1789
  }
1490
1790
 
@@ -1492,11 +1792,20 @@ export class ParallClient {
1492
1792
  return this.request('GET', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
1493
1793
  }
1494
1794
 
1495
- async createTaskComment(orgId: string, taskId: string, data: CreateTaskCommentRequest): Promise<TaskComment> {
1795
+ async createTaskComment(
1796
+ orgId: string,
1797
+ taskId: string,
1798
+ data: CreateTaskCommentRequest,
1799
+ ): Promise<TaskComment> {
1496
1800
  return this.request('POST', ENDPOINTS.TASK_COMMENTS(orgId, taskId), data);
1497
1801
  }
1498
1802
 
1499
- async updateTaskComment(orgId: string, taskId: string, commentId: string, data: UpdateTaskCommentRequest): Promise<TaskComment> {
1803
+ async updateTaskComment(
1804
+ orgId: string,
1805
+ taskId: string,
1806
+ commentId: string,
1807
+ data: UpdateTaskCommentRequest,
1808
+ ): Promise<TaskComment> {
1500
1809
  return this.request('PATCH', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId), data);
1501
1810
  }
1502
1811
 
@@ -1506,9 +1815,14 @@ export class ParallClient {
1506
1815
 
1507
1816
  // ---- Task Activities ----
1508
1817
 
1509
- async getTaskActivities(orgId: string, taskId: string, params?: {
1510
- limit?: number; cursor?: string;
1511
- }): Promise<PaginatedResponse<TaskActivity>> {
1818
+ async getTaskActivities(
1819
+ orgId: string,
1820
+ taskId: string,
1821
+ params?: {
1822
+ limit?: number;
1823
+ cursor?: string;
1824
+ },
1825
+ ): Promise<PaginatedResponse<TaskActivity>> {
1512
1826
  return this.request('GET', ENDPOINTS.TASK_ACTIVITIES(orgId, taskId), undefined, params);
1513
1827
  }
1514
1828
 
@@ -1527,7 +1841,11 @@ export class ParallClient {
1527
1841
  return this.request('GET', ENDPOINTS.PROJECT(orgId, projectId));
1528
1842
  }
1529
1843
 
1530
- async updateProject(orgId: string, projectId: string, data: UpdateProjectRequest): Promise<Project> {
1844
+ async updateProject(
1845
+ orgId: string,
1846
+ projectId: string,
1847
+ data: UpdateProjectRequest,
1848
+ ): Promise<Project> {
1531
1849
  return this.request('PATCH', ENDPOINTS.PROJECT(orgId, projectId), data);
1532
1850
  }
1533
1851
 
@@ -1541,15 +1859,27 @@ export class ParallClient {
1541
1859
  return this.request('POST', ENDPOINTS.SCHEDULES(orgId), input);
1542
1860
  }
1543
1861
 
1544
- async listSchedules(orgId: string, filters?: ScheduleFilters): Promise<PaginatedResponse<Schedule>> {
1545
- return this.request('GET', ENDPOINTS.SCHEDULES(orgId), undefined, filters as Record<string, string | number | undefined>);
1862
+ async listSchedules(
1863
+ orgId: string,
1864
+ filters?: ScheduleFilters,
1865
+ ): Promise<PaginatedResponse<Schedule>> {
1866
+ return this.request(
1867
+ 'GET',
1868
+ ENDPOINTS.SCHEDULES(orgId),
1869
+ undefined,
1870
+ filters as Record<string, string | number | undefined>,
1871
+ );
1546
1872
  }
1547
1873
 
1548
1874
  async getSchedule(orgId: string, scheduleId: string): Promise<Schedule> {
1549
1875
  return this.request('GET', ENDPOINTS.SCHEDULE(orgId, scheduleId));
1550
1876
  }
1551
1877
 
1552
- async updateSchedule(orgId: string, scheduleId: string, patch: UpdateScheduleInput): Promise<Schedule> {
1878
+ async updateSchedule(
1879
+ orgId: string,
1880
+ scheduleId: string,
1881
+ patch: UpdateScheduleInput,
1882
+ ): Promise<Schedule> {
1553
1883
  return this.request('PATCH', ENDPOINTS.SCHEDULE(orgId, scheduleId), patch);
1554
1884
  }
1555
1885
 
@@ -1601,31 +1931,59 @@ export class ParallClient {
1601
1931
  return this.request('GET', ENDPOINTS.WIKI(orgId, wikiId));
1602
1932
  }
1603
1933
 
1604
- async getWikiTree(orgId: string, wikiId: string, params?: { ref?: string; path?: string }): Promise<WikiTreeResponse> {
1934
+ async getWikiTree(
1935
+ orgId: string,
1936
+ wikiId: string,
1937
+ params?: { ref?: string; path?: string },
1938
+ ): Promise<WikiTreeResponse> {
1605
1939
  return this.request('GET', ENDPOINTS.WIKI_TREE(orgId, wikiId), undefined, params);
1606
1940
  }
1607
1941
 
1608
- async getWikiBlob(orgId: string, wikiId: string, params: { path: string; ref?: string }): Promise<WikiBlob> {
1942
+ async getWikiBlob(
1943
+ orgId: string,
1944
+ wikiId: string,
1945
+ params: { path: string; ref?: string },
1946
+ ): Promise<WikiBlob> {
1609
1947
  return this.request('GET', ENDPOINTS.WIKI_BLOB(orgId, wikiId), undefined, params);
1610
1948
  }
1611
1949
 
1612
- async getWikiNodeSections(orgId: string, wikiId: string, params?: { ref?: string }): Promise<WikiNodeSectionArtifact> {
1950
+ async getWikiNodeSections(
1951
+ orgId: string,
1952
+ wikiId: string,
1953
+ params?: { ref?: string },
1954
+ ): Promise<WikiNodeSectionArtifact> {
1613
1955
  return this.request('GET', ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), undefined, params);
1614
1956
  }
1615
1957
 
1616
- async searchWiki(orgId: string, wikiId: string, params: { q: string; limit?: number; path_prefix?: string; ref?: string }): Promise<WikiSearchResponse> {
1958
+ async searchWiki(
1959
+ orgId: string,
1960
+ wikiId: string,
1961
+ params: { q: string; limit?: number; path_prefix?: string; ref?: string },
1962
+ ): Promise<WikiSearchResponse> {
1617
1963
  return this.request('GET', ENDPOINTS.WIKI_SEARCH(orgId, wikiId), undefined, params);
1618
1964
  }
1619
1965
 
1620
- async getWikiPageIndex(orgId: string, wikiId: string, params?: { ref?: string }): Promise<WikiPageIndex> {
1966
+ async getWikiPageIndex(
1967
+ orgId: string,
1968
+ wikiId: string,
1969
+ params?: { ref?: string },
1970
+ ): Promise<WikiPageIndex> {
1621
1971
  return this.request('GET', ENDPOINTS.WIKI_PAGE_INDEX(orgId, wikiId), undefined, params);
1622
1972
  }
1623
1973
 
1624
- async getWikiRefs(orgId: string, wikiId: string, params?: { ref?: string }): Promise<WikiRefsResponse> {
1974
+ async getWikiRefs(
1975
+ orgId: string,
1976
+ wikiId: string,
1977
+ params?: { ref?: string },
1978
+ ): Promise<WikiRefsResponse> {
1625
1979
  return this.request('GET', ENDPOINTS.WIKI_REFS(orgId, wikiId), undefined, params);
1626
1980
  }
1627
1981
 
1628
- async checkWikiRefs(orgId: string, wikiId: string, params?: { ref?: string }): Promise<WikiRefsCheckResponse> {
1982
+ async checkWikiRefs(
1983
+ orgId: string,
1984
+ wikiId: string,
1985
+ params?: { ref?: string },
1986
+ ): Promise<WikiRefsCheckResponse> {
1629
1987
  return this.request('GET', ENDPOINTS.WIKI_REFS_CHECK(orgId, wikiId), undefined, params);
1630
1988
  }
1631
1989
 
@@ -1639,17 +1997,32 @@ export class ParallClient {
1639
1997
  return this.request('POST', ENDPOINTS.WIKI_ANCHOR_STATUS(orgId, wikiId), body);
1640
1998
  }
1641
1999
 
1642
- async createWikiChangeset(orgId: string, wikiId: string, data: CreateWikiChangesetRequest): Promise<WikiChangeset> {
1643
- return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId), data));
2000
+ async createWikiChangeset(
2001
+ orgId: string,
2002
+ wikiId: string,
2003
+ data: CreateWikiChangesetRequest,
2004
+ ): Promise<WikiChangeset> {
2005
+ return normalizeWikiChangeset(
2006
+ await this.request('POST', ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId), data),
2007
+ );
1644
2008
  }
1645
2009
 
1646
2010
  async getWikiChangesets(orgId: string, wikiId: string): Promise<WikiChangeset[]> {
1647
- const res = await this.request<{ data: WikiChangeset[] }>('GET', ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId));
2011
+ const res = await this.request<{ data: WikiChangeset[] }>(
2012
+ 'GET',
2013
+ ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId),
2014
+ );
1648
2015
  return res.data.map(normalizeWikiChangeset);
1649
2016
  }
1650
2017
 
1651
- async getWikiChangeset(orgId: string, wikiId: string, changesetId: string): Promise<WikiChangeset> {
1652
- return normalizeWikiChangeset(await this.request('GET', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId)));
2018
+ async getWikiChangeset(
2019
+ orgId: string,
2020
+ wikiId: string,
2021
+ changesetId: string,
2022
+ ): Promise<WikiChangeset> {
2023
+ return normalizeWikiChangeset(
2024
+ await this.request('GET', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId)),
2025
+ );
1653
2026
  }
1654
2027
 
1655
2028
  async updateWikiChangeset(
@@ -1658,19 +2031,37 @@ export class ParallClient {
1658
2031
  changesetId: string,
1659
2032
  data: UpdateWikiChangesetRequest,
1660
2033
  ): Promise<WikiChangeset> {
1661
- return normalizeWikiChangeset(await this.request('PATCH', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId), data));
2034
+ return normalizeWikiChangeset(
2035
+ await this.request('PATCH', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId), data),
2036
+ );
1662
2037
  }
1663
2038
 
1664
- async getWikiChangesetDiff(orgId: string, wikiId: string, changesetId: string): Promise<WikiDiff> {
2039
+ async getWikiChangesetDiff(
2040
+ orgId: string,
2041
+ wikiId: string,
2042
+ changesetId: string,
2043
+ ): Promise<WikiDiff> {
1665
2044
  return this.request('GET', ENDPOINTS.WIKI_CHANGESET_DIFF(orgId, wikiId, changesetId));
1666
2045
  }
1667
2046
 
1668
- async mergeWikiChangeset(orgId: string, wikiId: string, changesetId: string): Promise<WikiChangeset> {
1669
- return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESET_MERGE(orgId, wikiId, changesetId)));
2047
+ async mergeWikiChangeset(
2048
+ orgId: string,
2049
+ wikiId: string,
2050
+ changesetId: string,
2051
+ ): Promise<WikiChangeset> {
2052
+ return normalizeWikiChangeset(
2053
+ await this.request('POST', ENDPOINTS.WIKI_CHANGESET_MERGE(orgId, wikiId, changesetId)),
2054
+ );
1670
2055
  }
1671
2056
 
1672
- async closeWikiChangeset(orgId: string, wikiId: string, changesetId: string): Promise<WikiChangeset> {
1673
- return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESET_CLOSE(orgId, wikiId, changesetId)));
2057
+ async closeWikiChangeset(
2058
+ orgId: string,
2059
+ wikiId: string,
2060
+ changesetId: string,
2061
+ ): Promise<WikiChangeset> {
2062
+ return normalizeWikiChangeset(
2063
+ await this.request('POST', ENDPOINTS.WIKI_CHANGESET_CLOSE(orgId, wikiId, changesetId)),
2064
+ );
1674
2065
  }
1675
2066
 
1676
2067
  // ---- Wiki Binary Upload / Delete (Phase 2 of wiki-file-storage-design) ----
@@ -1727,12 +2118,10 @@ export class ParallClient {
1727
2118
  wikiId: string,
1728
2119
  params: { path: string; message?: string },
1729
2120
  ): Promise<void> {
1730
- await this.request(
1731
- 'DELETE',
1732
- ENDPOINTS.WIKI_FILES(orgId, wikiId),
1733
- undefined,
1734
- { path: params.path, message: params.message },
1735
- );
2121
+ await this.request('DELETE', ENDPOINTS.WIKI_FILES(orgId, wikiId), undefined, {
2122
+ path: params.path,
2123
+ message: params.message,
2124
+ });
1736
2125
  }
1737
2126
 
1738
2127
  /**
@@ -1745,21 +2134,24 @@ export class ParallClient {
1745
2134
  wikiId: string,
1746
2135
  params: { path: string; ref?: string },
1747
2136
  ): Promise<WikiFilePreviewUrlResponse> {
1748
- return this.request(
1749
- 'POST',
1750
- ENDPOINTS.WIKI_FILE_PREVIEW_URL(orgId, wikiId),
1751
- params,
1752
- );
2137
+ return this.request('POST', ENDPOINTS.WIKI_FILE_PREVIEW_URL(orgId, wikiId), params);
1753
2138
  }
1754
2139
 
1755
2140
  // ---- Wiki Path Scopes (AFCS ACL) ----
1756
2141
 
1757
2142
  async getWikiPathScopes(orgId: string, wikiId: string): Promise<WikiPathScope[]> {
1758
- const res = await this.request<{ data: WikiPathScope[] }>('GET', ENDPOINTS.WIKI_PATH_SCOPES(orgId, wikiId));
2143
+ const res = await this.request<{ data: WikiPathScope[] }>(
2144
+ 'GET',
2145
+ ENDPOINTS.WIKI_PATH_SCOPES(orgId, wikiId),
2146
+ );
1759
2147
  return res.data;
1760
2148
  }
1761
2149
 
1762
- async createWikiPathScope(orgId: string, wikiId: string, data: CreateWikiPathScopeRequest): Promise<WikiPathScope> {
2150
+ async createWikiPathScope(
2151
+ orgId: string,
2152
+ wikiId: string,
2153
+ data: CreateWikiPathScopeRequest,
2154
+ ): Promise<WikiPathScope> {
1763
2155
  return this.request('POST', ENDPOINTS.WIKI_PATH_SCOPES(orgId, wikiId), data);
1764
2156
  }
1765
2157
 
@@ -1767,11 +2159,24 @@ export class ParallClient {
1767
2159
  await this.request('DELETE', ENDPOINTS.WIKI_PATH_SCOPE(orgId, wikiId, scopeId));
1768
2160
  }
1769
2161
 
1770
- async getWikiAccessStatus(orgId: string, wikiId: string, path?: string): Promise<WikiAccessStatus> {
1771
- return this.request('GET', ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), undefined, path ? { path } : undefined);
2162
+ async getWikiAccessStatus(
2163
+ orgId: string,
2164
+ wikiId: string,
2165
+ path?: string,
2166
+ ): Promise<WikiAccessStatus> {
2167
+ return this.request(
2168
+ 'GET',
2169
+ ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId),
2170
+ undefined,
2171
+ path ? { path } : undefined,
2172
+ );
1772
2173
  }
1773
2174
 
1774
- async createWikiAccessRequest(orgId: string, wikiId: string, data: CreateWikiAccessRequest): Promise<void> {
2175
+ async createWikiAccessRequest(
2176
+ orgId: string,
2177
+ wikiId: string,
2178
+ data: CreateWikiAccessRequest,
2179
+ ): Promise<void> {
1775
2180
  await this.request('POST', ENDPOINTS.WIKI_ACCESS_REQUESTS(orgId, wikiId), data);
1776
2181
  }
1777
2182
 
@@ -1791,7 +2196,10 @@ export class ParallClient {
1791
2196
  path: string,
1792
2197
  params?: { page?: number; limit?: number },
1793
2198
  ): Promise<{ data: WikiCommit[]; page: number; limit: number }> {
1794
- return this.request('GET', ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), undefined, { path, ...params });
2199
+ return this.request('GET', ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), undefined, {
2200
+ path,
2201
+ ...params,
2202
+ });
1795
2203
  }
1796
2204
 
1797
2205
  async getWikiBlame(
@@ -1826,8 +2234,20 @@ export class ParallClient {
1826
2234
  async getComments(
1827
2235
  orgId: string,
1828
2236
  params:
1829
- | { target_uri: string; target_prefix?: never; limit?: number; cursor?: string; order?: string }
1830
- | { target_prefix: string; target_uri?: never; limit?: number; cursor?: string; order?: string },
2237
+ | {
2238
+ target_uri: string;
2239
+ target_prefix?: never;
2240
+ limit?: number;
2241
+ cursor?: string;
2242
+ order?: string;
2243
+ }
2244
+ | {
2245
+ target_prefix: string;
2246
+ target_uri?: never;
2247
+ limit?: number;
2248
+ cursor?: string;
2249
+ order?: string;
2250
+ },
1831
2251
  ): Promise<PaginatedResponse<Comment>> {
1832
2252
  return this.request('GET', ENDPOINTS.COMMENTS(orgId), undefined, params);
1833
2253
  }
@@ -1840,7 +2260,11 @@ export class ParallClient {
1840
2260
  return this.request('POST', ENDPOINTS.COMMENTS(orgId), data);
1841
2261
  }
1842
2262
 
1843
- async updateComment(orgId: string, commentId: string, data: UpdateCommentRequest): Promise<Comment> {
2263
+ async updateComment(
2264
+ orgId: string,
2265
+ commentId: string,
2266
+ data: UpdateCommentRequest,
2267
+ ): Promise<Comment> {
1844
2268
  return this.request('PATCH', ENDPOINTS.COMMENT(orgId, commentId), data);
1845
2269
  }
1846
2270
 
@@ -1850,7 +2274,11 @@ export class ParallClient {
1850
2274
 
1851
2275
  // ---- References ----
1852
2276
 
1853
- async resolveRefs(orgId: string, refs: string[], options?: { sourceMessageId?: string }): Promise<ResolveRefsResponse> {
2277
+ async resolveRefs(
2278
+ orgId: string,
2279
+ refs: string[],
2280
+ options?: { sourceMessageId?: string },
2281
+ ): Promise<ResolveRefsResponse> {
1854
2282
  const body: Record<string, unknown> = { refs };
1855
2283
  if (options?.sourceMessageId) {
1856
2284
  body.source_message_id = options.sourceMessageId;
@@ -1889,7 +2317,9 @@ export class ParallClient {
1889
2317
  return this.request('GET', ENDPOINTS.NOTIFICATION_PREFERENCES);
1890
2318
  }
1891
2319
 
1892
- async updateNotificationPreferences(prefs: Partial<NotifPrefs>): Promise<NotificationPreferences> {
2320
+ async updateNotificationPreferences(
2321
+ prefs: Partial<NotifPrefs>,
2322
+ ): Promise<NotificationPreferences> {
1893
2323
  return this.request('PATCH', ENDPOINTS.NOTIFICATION_PREFERENCES, { prefs });
1894
2324
  }
1895
2325
 
@@ -1905,7 +2335,20 @@ export class ParallClient {
1905
2335
  return this.request('GET', ENDPOINTS.BILLING(orgId));
1906
2336
  }
1907
2337
 
1908
- async listBillingTransactions(orgId: string, opts?: { cursor?: string; limit?: number; types?: string[]; user_id?: string; unresolved_actor?: boolean; machine_id?: string; unresolved_machine?: boolean; from?: string; to?: string }): Promise<{ items: CreditTransaction[]; next_cursor: string | null }> {
2338
+ async listBillingTransactions(
2339
+ orgId: string,
2340
+ opts?: {
2341
+ cursor?: string;
2342
+ limit?: number;
2343
+ types?: string[];
2344
+ user_id?: string;
2345
+ unresolved_actor?: boolean;
2346
+ machine_id?: string;
2347
+ unresolved_machine?: boolean;
2348
+ from?: string;
2349
+ to?: string;
2350
+ },
2351
+ ): Promise<{ items: CreditTransaction[]; next_cursor: string | null }> {
1909
2352
  const params = new URLSearchParams();
1910
2353
  if (opts?.cursor) params.set('cursor', opts.cursor);
1911
2354
  if (opts?.limit) params.set('limit', String(opts.limit));
@@ -1920,14 +2363,20 @@ export class ParallClient {
1920
2363
  return this.request('GET', `${ENDPOINTS.BILLING_TRANSACTIONS(orgId)}${qs ? `?${qs}` : ''}`);
1921
2364
  }
1922
2365
 
1923
- async listBillingTransactionAgentGroups(orgId: string, opts?: { from?: string; to?: string }): Promise<{ groups: CreditTransactionAgentGroup[] }> {
2366
+ async listBillingTransactionAgentGroups(
2367
+ orgId: string,
2368
+ opts?: { from?: string; to?: string },
2369
+ ): Promise<{ groups: CreditTransactionAgentGroup[] }> {
1924
2370
  const params = new URLSearchParams({ group_by: 'agent' });
1925
2371
  if (opts?.from) params.set('from', opts.from);
1926
2372
  if (opts?.to) params.set('to', opts.to);
1927
2373
  return this.request('GET', `${ENDPOINTS.BILLING_TRANSACTIONS(orgId)}?${params}`);
1928
2374
  }
1929
2375
 
1930
- async listBillingTransactionMachineGroups(orgId: string, opts?: { from?: string; to?: string }): Promise<{ groups: CreditTransactionMachineGroup[] }> {
2376
+ async listBillingTransactionMachineGroups(
2377
+ orgId: string,
2378
+ opts?: { from?: string; to?: string },
2379
+ ): Promise<{ groups: CreditTransactionMachineGroup[] }> {
1931
2380
  const params = new URLSearchParams({ group_by: 'machine' });
1932
2381
  if (opts?.from) params.set('from', opts.from);
1933
2382
  if (opts?.to) params.set('to', opts.to);
@@ -1953,7 +2402,10 @@ export class ParallClient {
1953
2402
  return this.request('GET', ENDPOINTS.BILLING_AUTO_RELOAD(orgId));
1954
2403
  }
1955
2404
 
1956
- async updateAutoReloadSettings(orgId: string, req: UpdateAutoReloadRequest): Promise<AutoReloadSettings> {
2405
+ async updateAutoReloadSettings(
2406
+ orgId: string,
2407
+ req: UpdateAutoReloadRequest,
2408
+ ): Promise<AutoReloadSettings> {
1957
2409
  return this.request('PUT', ENDPOINTS.BILLING_AUTO_RELOAD(orgId), req);
1958
2410
  }
1959
2411
 
@@ -1964,7 +2416,78 @@ export class ParallClient {
1964
2416
  // own thin client (`ts/admin/lib/api.ts`) that hits these directly.
1965
2417
 
1966
2418
  async getComputePricing(): Promise<ComputePricingResponse> {
1967
- const resp = await this.request<{ data: ComputePricingResponse }>('GET', ENDPOINTS.COMPUTE_PRICING());
2419
+ const resp = await this.request<{ data: ComputePricingResponse }>(
2420
+ 'GET',
2421
+ ENDPOINTS.COMPUTE_PRICING(),
2422
+ );
2423
+ return resp.data;
2424
+ }
2425
+
2426
+ // ---- Clips ----
2427
+
2428
+ async listClips(orgId: string): Promise<Clip[]> {
2429
+ const resp = await this.request<{ data: Clip[] }>('GET', ENDPOINTS.CLIPS(orgId));
2430
+ return resp.data;
2431
+ }
2432
+
2433
+ async createClip(orgId: string, req: CreateClipRequest): Promise<Clip> {
2434
+ return this.request('POST', ENDPOINTS.CLIPS(orgId), req);
2435
+ }
2436
+
2437
+ async getClip(orgId: string, clipId: string): Promise<Clip> {
2438
+ return this.request('GET', ENDPOINTS.CLIP(orgId, clipId));
2439
+ }
2440
+
2441
+ async updateClip(orgId: string, clipId: string, req: UpdateClipRequest): Promise<Clip> {
2442
+ return this.request('PATCH', ENDPOINTS.CLIP(orgId, clipId), req);
2443
+ }
2444
+
2445
+ async deleteClip(orgId: string, clipId: string): Promise<void> {
2446
+ await this.request('DELETE', ENDPOINTS.CLIP(orgId, clipId));
2447
+ }
2448
+
2449
+ async listClipAgents(orgId: string, clipId: string): Promise<AgentClip[]> {
2450
+ const resp = await this.request<{ data: AgentClip[] }>(
2451
+ 'GET',
2452
+ ENDPOINTS.CLIP_AGENTS(orgId, clipId),
2453
+ );
2454
+ return resp.data;
2455
+ }
2456
+
2457
+ async listAgentClips(orgId: string, agentId: string): Promise<Clip[]> {
2458
+ const resp = await this.request<{ data: Clip[] }>('GET', ENDPOINTS.AGENT_CLIPS(orgId, agentId));
2459
+ return resp.data;
2460
+ }
2461
+
2462
+ async bindAgentClip(
2463
+ orgId: string,
2464
+ agentId: string,
2465
+ req: BindAgentClipRequest,
2466
+ ): Promise<AgentClip> {
2467
+ return this.request('POST', ENDPOINTS.AGENT_CLIPS(orgId, agentId), req);
2468
+ }
2469
+
2470
+ async unbindAgentClip(orgId: string, agentId: string, clipId: string): Promise<void> {
2471
+ await this.request('DELETE', ENDPOINTS.AGENT_CLIP(orgId, agentId, clipId));
2472
+ }
2473
+
2474
+ async invokeClip(orgId: string, req: InvokeClipRequest): Promise<InvokeClipResponse> {
2475
+ const serverTimeout = Math.max(req.timeout_ms ?? 30000, 1000);
2476
+ const timeoutMs = serverTimeout + 5000;
2477
+ return this.request('POST', ENDPOINTS.CLIP_INVOKE(orgId), req, undefined, false, { timeoutMs });
2478
+ }
2479
+
2480
+ async listOnlineClips(orgId: string): Promise<OnlineClipInfo[]> {
2481
+ const resp = await this.request<{ data: OnlineClipInfo[] }>(
2482
+ 'GET',
2483
+ ENDPOINTS.CLIP_ONLINE(orgId),
2484
+ );
2485
+ return resp.data;
2486
+ }
2487
+
2488
+ async listRegistryClips(q?: string): Promise<RegistryClipInfo[]> {
2489
+ const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : '');
2490
+ const resp = await this.request<{ data: RegistryClipInfo[] }>('GET', url);
1968
2491
  return resp.data;
1969
2492
  }
1970
2493
  }
@@ -1979,6 +2502,12 @@ function normalizeWikiChangeset(changeset: WikiChangeset): WikiChangeset {
1979
2502
 
1980
2503
  export class ApiError extends Error {
1981
2504
  extras?: Record<string, unknown>;
2505
+ /** Attempted action (authorization denials) — e.g. "chat.add_member". */
2506
+ action?: string;
2507
+ /** Target resource URI that was evaluated — e.g. "prll://cht_…". */
2508
+ resourceUri?: string;
2509
+ /** Whether the action is approval-executable (PERMISSION_DENIED only). */
2510
+ approvable?: boolean;
1982
2511
 
1983
2512
  constructor(
1984
2513
  public status: number,
@@ -1989,3 +2518,57 @@ export class ApiError extends Error {
1989
2518
  this.name = 'ApiError';
1990
2519
  }
1991
2520
  }
2521
+
2522
+ /**
2523
+ * Parse a non-2xx response body into a typed ApiError, losslessly.
2524
+ *
2525
+ * The wire envelope is `{ "error": { code, message, status, action?,
2526
+ * resource_uri?, approvable? } }`. We read message/code/anchors from the
2527
+ * nested `error` object, with a fallback to a legacy flat shape, and never
2528
+ * silently drop to HTTP status text when a real message is present.
2529
+ * See docs/engineering-design/error-contract-design.md
2530
+ */
2531
+ function buildApiError(
2532
+ res: { status: number; statusText: string },
2533
+ rawErrorBody: unknown,
2534
+ ): ApiError {
2535
+ const errorBody =
2536
+ rawErrorBody !== null && typeof rawErrorBody === 'object'
2537
+ ? (rawErrorBody as Record<string, unknown>)
2538
+ : {};
2539
+ const errorObj =
2540
+ errorBody.error && typeof errorBody.error === 'object'
2541
+ ? (errorBody.error as Record<string, unknown>)
2542
+ : undefined;
2543
+ // message/code live under `error`. Tolerate two legacy shapes from
2544
+ // not-yet-migrated services: a flat `{code, message, ...}` (read top-level
2545
+ // message/code) and a bare `{error: "text"}`. Never silently drop to
2546
+ // res.statusText when the server provided a real message.
2547
+ const errMsg =
2548
+ (typeof errorObj?.message === 'string' ? errorObj.message : undefined) ??
2549
+ (typeof errorBody.message === 'string' ? (errorBody.message as string) : undefined) ??
2550
+ (typeof errorBody.error === 'string' ? (errorBody.error as string) : undefined);
2551
+ const errCode =
2552
+ (typeof errorObj?.code === 'string' ? errorObj.code : undefined) ??
2553
+ (typeof errorBody.code === 'string' ? (errorBody.code as string) : undefined);
2554
+ const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
2555
+ // Machine anchors: present under `error`, with a legacy flat fallback.
2556
+ const anchors = errorObj ?? errorBody;
2557
+ if (typeof anchors.action === 'string') apiError.action = anchors.action;
2558
+ if (typeof anchors.resource_uri === 'string') apiError.resourceUri = anchors.resource_uri;
2559
+ if (typeof anchors.approvable === 'boolean') apiError.approvable = anchors.approvable as boolean;
2560
+ // Preserve any other fields — from both the top level and the nested `error`
2561
+ // object — so additive/unknown server fields survive (transparent mirror;
2562
+ // keeps ApiError.extras meaningful for third-party consumers).
2563
+ // The anchors (action/resource_uri/approvable) are exposed as typed fields
2564
+ // above but ALSO kept in `extras` for one compatibility window, so consumers
2565
+ // written against the old `err.extras?.action` shape don't break on upgrade.
2566
+ // Only the envelope-structural keys are excluded.
2567
+ const structural = new Set(['error', 'code', 'message', 'status']);
2568
+ const extras: Record<string, unknown> = {};
2569
+ for (const [k, v] of Object.entries(errorBody)) if (!structural.has(k)) extras[k] = v;
2570
+ if (errorObj)
2571
+ for (const [k, v] of Object.entries(errorObj)) if (!structural.has(k)) extras[k] = v;
2572
+ if (Object.keys(extras).length > 0) apiError.extras = extras;
2573
+ return apiError;
2574
+ }