@pnds/sdk 0.1.0 → 0.2.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/src/types.ts CHANGED
@@ -12,10 +12,6 @@ export interface User {
12
12
  avatar_url: string | null;
13
13
  email: string | null;
14
14
  phone: string | null;
15
- agent_provider: string | null;
16
- agent_endpoint: string | null;
17
- agent_model: string | null;
18
- agent_permissions: string[];
19
15
  status: UserStatus;
20
16
  email_verified: boolean;
21
17
  last_seen_at: string | null;
@@ -55,12 +51,12 @@ export interface ChatMember {
55
51
 
56
52
  export type MessageType =
57
53
  | 'text'
58
- | 'image'
59
54
  | 'file'
60
- | 'tool_call'
61
- | 'tool_result'
62
- | 'approval_card'
63
- | 'state_delta'
55
+ | 'tool_call' // deprecated: use AgentStep
56
+ | 'tool_result' // deprecated: use AgentStep
57
+ | 'approval_card' // deprecated: use 'card' with card_type='approval'
58
+ | 'state_delta' // deprecated: use AgentStep
59
+ | 'card'
64
60
  | 'system';
65
61
 
66
62
  export const MENTION_ALL_USER_ID = 'all';
@@ -90,8 +86,6 @@ export interface MediaContent {
90
86
  file_name: string;
91
87
  file_size: number;
92
88
  mime_type: string;
93
- url: string;
94
- thumbnail_url?: string;
95
89
  width?: number;
96
90
  height?: number;
97
91
  duration?: number;
@@ -132,6 +126,12 @@ export interface ApprovalCardContent {
132
126
  expires_at: string;
133
127
  }
134
128
 
129
+ // CardContent is the polymorphic content for 'card' message type.
130
+ // card_type discriminates the data payload.
131
+ export type CardContent =
132
+ | { card_type: 'approval'; data: ApprovalCardContent; approval_id?: string }
133
+ | { card_type: string; data: Record<string, unknown>; approval_id?: string };
134
+
135
135
  export type AgentState = 'thinking' | 'tool_calling' | 'streaming' | 'waiting_approval' | 'completed' | 'error';
136
136
 
137
137
  export interface StateDeltaContent {
@@ -142,6 +142,7 @@ export interface StateDeltaContent {
142
142
  }
143
143
 
144
144
  export type SystemEvent =
145
+ | 'chat_created'
145
146
  | 'member_joined'
146
147
  | 'member_left'
147
148
  | 'member_role_changed'
@@ -164,7 +165,8 @@ export type MessageContent =
164
165
  | ApprovalCardContent
165
166
  | StateDeltaContent
166
167
  | SystemContent
167
- | Record<string, unknown>;
168
+ | IssueProjectionContent
169
+ | CardContent;
168
170
 
169
171
  export interface Message {
170
172
  id: string;
@@ -180,19 +182,20 @@ export interface Message {
180
182
  deleted_at: string | null;
181
183
  created_at: string;
182
184
  idempotency_key: string | null;
185
+ agent_run_id?: string | null;
183
186
  }
184
187
 
185
188
  export interface Attachment {
186
189
  id: string;
187
- message_id: string | null;
190
+ org_id: string;
188
191
  uploader_id: string;
189
192
  file_name: string;
190
193
  file_size: number;
191
194
  mime_type: string;
192
195
  storage_key: string;
196
+ status: 'pending' | 'uploaded';
193
197
  width: number | null;
194
198
  height: number | null;
195
- duration: number | null;
196
199
  thumbnail_key: string | null;
197
200
  created_at: string;
198
201
  }
@@ -231,6 +234,39 @@ export interface OrgMember {
231
234
  user?: User;
232
235
  }
233
236
 
237
+ // Invitation
238
+
239
+ export type InvitationStatus = 'pending' | 'accepted' | 'declined';
240
+
241
+ export interface OrgInvitation {
242
+ id: string;
243
+ org_id: string;
244
+ email: string;
245
+ role: OrgMemberRole;
246
+ status: InvitationStatus;
247
+ inviter_id: string;
248
+ expires_at: string;
249
+ accepted_at: string | null;
250
+ created_at: string;
251
+ inviter?: User;
252
+ org?: Organization;
253
+ }
254
+
255
+ export interface InvitationPublicInfo {
256
+ id: string;
257
+ org_name: string;
258
+ org_avatar_url: string | null;
259
+ inviter_name: string;
260
+ email: string;
261
+ role: OrgMemberRole;
262
+ expires_at: string;
263
+ }
264
+
265
+ export interface CreateInvitationRequest {
266
+ email: string;
267
+ role?: OrgMemberRole;
268
+ }
269
+
234
270
  export interface ApiKey {
235
271
  id: string;
236
272
  key: string;
@@ -241,6 +277,7 @@ export interface ApiKey {
241
277
  export interface CreateAgentResponse {
242
278
  user: User;
243
279
  api_key: string;
280
+ machine?: Machine;
244
281
  }
245
282
 
246
283
  export interface UnreadCount {
@@ -269,9 +306,11 @@ export interface ErrorResponse {
269
306
  export interface AuthTokens {
270
307
  access_token: string;
271
308
  refresh_token: string;
272
- expires_in: number;
273
309
  user: User;
274
- is_new_user?: boolean;
310
+ }
311
+
312
+ export interface RegisterResponse {
313
+ email: string;
275
314
  }
276
315
 
277
316
  export interface CheckEmailResponse {
@@ -318,6 +357,7 @@ export interface SendMessageRequest {
318
357
  content: MessageContent;
319
358
  thread_root_id?: string;
320
359
  idempotency_key?: string;
360
+ agent_run_id?: string;
321
361
  }
322
362
 
323
363
  export interface PatchMessageRequest {
@@ -337,10 +377,6 @@ export interface WorkspaceFiles {
337
377
  tools_md?: string;
338
378
  }
339
379
 
340
- export interface HostedStatus {
341
- state: 'running' | 'stopped' | 'creating' | 'error';
342
- }
343
-
344
380
  export interface CreateAgentRequest {
345
381
  display_name: string;
346
382
  agent_provider?: string;
@@ -349,10 +385,25 @@ export interface CreateAgentRequest {
349
385
  agent_permissions?: string[];
350
386
  machine_type?: string;
351
387
  machine_label?: string;
352
- machine_host?: string;
353
388
  workspace?: WorkspaceFiles;
354
389
  }
355
390
 
391
+ // ---- Machine types ----
392
+
393
+ export type MachineStatus = 'provisioning' | 'starting' | 'running' | 'stopped' | 'error' | 'terminated';
394
+
395
+ export interface Machine {
396
+ id: string;
397
+ org_id: string;
398
+ label: string;
399
+ status: MachineStatus;
400
+ compute_provider: string;
401
+ region: string | null;
402
+ created_by: string;
403
+ created_at: string;
404
+ updated_at: string;
405
+ }
406
+
356
407
  // ---- Agent enriched types ----
357
408
 
358
409
  export interface AgentProfile {
@@ -361,12 +412,7 @@ export interface AgentProfile {
361
412
  endpoint: string | null;
362
413
  model: string | null;
363
414
  permissions: string[];
364
- machine_type: string | null;
365
- machine_label: string | null;
366
- machine_host: string | null;
367
- compute_provider: string | null;
368
- compute_resource_id: string | null;
369
- compute_metadata: Record<string, string> | null;
415
+ machine_id: string | null;
370
416
  created_by: string;
371
417
  created_at: string;
372
418
  }
@@ -387,6 +433,7 @@ export interface AgentPresence {
387
433
 
388
434
  export interface AgentWithRuntime extends User {
389
435
  agent_profile?: AgentProfile | null;
436
+ machine?: Machine | null;
390
437
  presence?: AgentPresence | null;
391
438
  }
392
439
 
@@ -396,6 +443,11 @@ export interface PresignUploadRequest {
396
443
  mime_type: string;
397
444
  }
398
445
 
446
+ export interface FileUrlResponse {
447
+ url: string;
448
+ expires_in: number;
449
+ }
450
+
399
451
  // ============================================================
400
452
  // Task Types
401
453
  // ============================================================
@@ -415,7 +467,12 @@ export interface Task {
415
467
  assignee_id: string | null;
416
468
  creator_id: string;
417
469
  parent_id: string | null;
470
+ project_id: string | null;
471
+ seq_number: number | null;
472
+ identifier: string | null;
418
473
  sort_order: number;
474
+ assignee?: User;
475
+ creator?: User;
419
476
  created_at: string;
420
477
  updated_at: string;
421
478
  started_at: string | null;
@@ -423,6 +480,20 @@ export interface Task {
423
480
  canceled_at: string | null;
424
481
  }
425
482
 
483
+ /** Content shape for system messages that project issue operations into chat. */
484
+ export interface IssueProjectionContent {
485
+ event: 'issue_created' | 'issue_status_changed' | 'issue_assigned';
486
+ actor_id: string;
487
+ actor_name: string;
488
+ issue_id: string;
489
+ identifier: string;
490
+ title: string;
491
+ old_status?: string;
492
+ new_status?: string;
493
+ assignee_id?: string;
494
+ assignee_name?: string;
495
+ }
496
+
426
497
  export interface TaskRelation {
427
498
  id: string;
428
499
  task_id: string;
@@ -436,9 +507,12 @@ export interface TaskRelation {
436
507
  export interface CreateTaskRequest {
437
508
  title: string;
438
509
  description?: string;
510
+ status?: TaskStatus;
439
511
  priority?: TaskPriority;
440
512
  assignee_id?: string;
441
513
  parent_id?: string;
514
+ project_id?: string;
515
+ source_chat_id?: string;
442
516
  sort_order?: number;
443
517
  }
444
518
 
@@ -448,6 +522,8 @@ export interface UpdateTaskRequest {
448
522
  status?: TaskStatus;
449
523
  priority?: TaskPriority;
450
524
  assignee_id?: string | null;
525
+ parent_id?: string | null;
526
+ project_id?: string | null;
451
527
  sort_order?: number;
452
528
  }
453
529
 
@@ -457,6 +533,44 @@ export interface CreateTaskRelationRequest {
457
533
  relation_type: TaskRelationType;
458
534
  }
459
535
 
536
+ // ============================================================
537
+ // Project Types
538
+ // ============================================================
539
+
540
+ export type ProjectStatus = 'active' | 'paused' | 'completed' | 'archived';
541
+
542
+ export interface Project {
543
+ id: string;
544
+ org_id: string;
545
+ name: string;
546
+ key: string;
547
+ description: string | null;
548
+ lead_id: string | null;
549
+ status: ProjectStatus;
550
+ color: string | null;
551
+ sort_order: number;
552
+ created_at: string;
553
+ updated_at: string;
554
+ }
555
+
556
+ export interface CreateProjectRequest {
557
+ name: string;
558
+ key?: string;
559
+ description?: string;
560
+ lead_id?: string;
561
+ color?: string;
562
+ }
563
+
564
+ export interface UpdateProjectRequest {
565
+ name?: string;
566
+ key?: string;
567
+ description?: string | null;
568
+ lead_id?: string | null;
569
+ status?: ProjectStatus;
570
+ color?: string | null;
571
+ sort_order?: number;
572
+ }
573
+
460
574
  // ============================================================
461
575
  // WebSocket Event Types
462
576
  // ============================================================
@@ -485,6 +599,11 @@ export interface MessageNewData {
485
599
  content: MessageContent;
486
600
  version: number;
487
601
  created_at: string;
602
+ reply_count?: number;
603
+ edited_at?: string | null;
604
+ deleted_at?: string | null;
605
+ idempotency_key?: string | null;
606
+ agent_run_id?: string | null;
488
607
  }
489
608
 
490
609
  export interface MessagePatchData {
@@ -515,16 +634,20 @@ export interface TypingUpdateData {
515
634
 
516
635
  export interface ChatUpdateData {
517
636
  chat_id: string;
518
- changes: Record<string, unknown>;
637
+ changes: Partial<Pick<Chat, 'name' | 'avatar_url' | 'description' | 'last_message_at' | 'last_message_preview' | 'last_message_sender'>>;
519
638
  }
520
639
 
521
- export interface ApprovalUpdateData {
522
- approval_id: string;
640
+ /** @deprecated Use CardUpdateData */
641
+ export type ApprovalUpdateData = CardUpdateData;
642
+
643
+ export interface CardUpdateData {
644
+ card_type: string;
645
+ approval_id?: string;
523
646
  message_id: string;
524
647
  chat_id: string;
525
- status: ApprovalStatus;
526
- decided_by: string;
527
- decided_at: string;
648
+ status: string;
649
+ decided_by?: string;
650
+ decided_at?: string;
528
651
  }
529
652
 
530
653
  export interface RecoveryOverflowData {
@@ -541,6 +664,40 @@ export interface MembershipChangedData {
541
664
  action: 'added' | 'removed';
542
665
  }
543
666
 
667
+ // ---- Task Comment & Activity Types ----
668
+
669
+ export type TaskActivityAction = 'created' | 'field_changed' | 'commented';
670
+
671
+ export interface TaskComment {
672
+ id: string;
673
+ task_id: string;
674
+ author_id: string;
675
+ body: string;
676
+ created_at: string;
677
+ updated_at: string;
678
+ author?: User;
679
+ }
680
+
681
+ export interface TaskActivity {
682
+ id: string;
683
+ task_id: string;
684
+ actor_id: string;
685
+ action: TaskActivityAction;
686
+ field?: string;
687
+ old_value?: string;
688
+ new_value?: string;
689
+ created_at: string;
690
+ actor?: User;
691
+ }
692
+
693
+ export interface CreateTaskCommentRequest {
694
+ body: string;
695
+ }
696
+
697
+ export interface UpdateTaskCommentRequest {
698
+ body: string;
699
+ }
700
+
544
701
  // ---- Task WS Event Data ----
545
702
 
546
703
  export type TaskCreatedData = Task;
@@ -550,6 +707,72 @@ export interface TaskDeletedData {
550
707
  org_id: string;
551
708
  }
552
709
 
710
+ export type TaskCommentCreatedData = TaskComment;
711
+ export type TaskCommentUpdatedData = TaskComment;
712
+ export interface TaskCommentDeletedData {
713
+ comment_id: string;
714
+ task_id: string;
715
+ org_id: string;
716
+ }
717
+
718
+ export type ProjectCreatedData = Project;
719
+ export type ProjectUpdatedData = Project;
720
+ export interface ProjectDeletedData {
721
+ project_id: string;
722
+ org_id: string;
723
+ }
724
+
725
+ // ---- Agent Run / Step ----
726
+
727
+ export interface AgentRunDB {
728
+ id: string;
729
+ agent_id: string;
730
+ org_id: string;
731
+ chat_id: string | null;
732
+ status: string;
733
+ trigger_type: string;
734
+ trigger_ref: Record<string, unknown> | null;
735
+ started_at: string;
736
+ finished_at: string | null;
737
+ created_at: string;
738
+ }
739
+
740
+ export interface AgentStep {
741
+ id: string;
742
+ run_id: string;
743
+ step_type: string;
744
+ content: Record<string, unknown>;
745
+ created_at: string;
746
+ }
747
+
748
+ export interface CreateAgentRunRequest {
749
+ trigger_type: string;
750
+ trigger_ref?: Record<string, unknown>;
751
+ chat_id?: string;
752
+ }
753
+
754
+ export interface CreateAgentStepRequest {
755
+ step_type: string;
756
+ content: Record<string, unknown>;
757
+ }
758
+
759
+ export interface UpdateAgentRunRequest {
760
+ status: string;
761
+ }
762
+
763
+ // Agent run WS event data
764
+ export type AgentRunNewData = AgentRunDB;
765
+
766
+ export interface AgentRunUpdateData {
767
+ run_id: string;
768
+ status: string;
769
+ finished_at?: string;
770
+ }
771
+
772
+ export interface AgentStepNewData extends AgentStep {
773
+ chat_id: string;
774
+ }
775
+
553
776
  export type WsEventMap = {
554
777
  'hello': HelloData;
555
778
  'message.new': MessageNewData;
@@ -558,7 +781,8 @@ export type WsEventMap = {
558
781
  'message.delete': MessageDeleteData;
559
782
  'typing.update': TypingUpdateData;
560
783
  'chat.update': ChatUpdateData;
561
- 'approval.update': ApprovalUpdateData;
784
+ 'approval.update': ApprovalUpdateData; // deprecated, kept for backward compat
785
+ 'card.update': CardUpdateData;
562
786
  'recovery.overflow': RecoveryOverflowData;
563
787
  'watching': WatchingData;
564
788
  'pong': { ts: number };
@@ -566,4 +790,56 @@ export type WsEventMap = {
566
790
  'task.created': TaskCreatedData;
567
791
  'task.updated': TaskUpdatedData;
568
792
  'task.deleted': TaskDeletedData;
793
+ 'task.comment.created': TaskCommentCreatedData;
794
+ 'task.comment.updated': TaskCommentUpdatedData;
795
+ 'task.comment.deleted': TaskCommentDeletedData;
796
+ 'project.created': ProjectCreatedData;
797
+ 'project.updated': ProjectUpdatedData;
798
+ 'project.deleted': ProjectDeletedData;
799
+ 'agent_run.new': AgentRunNewData;
800
+ 'agent_run.update': AgentRunUpdateData;
801
+ 'agent_step.new': AgentStepNewData;
802
+ 'invitation.new': InvitationNewEventData;
803
+ 'invitation.accepted': InvitationAcceptedEventData;
804
+ 'invitation.declined': InvitationDeclinedEventData;
805
+ 'agent_config.update': AgentConfigUpdateData;
569
806
  };
807
+
808
+ // Invitation WS event data
809
+ export interface InvitationNewEventData {
810
+ id: string;
811
+ org_id: string;
812
+ org_name: string;
813
+ inviter_name: string;
814
+ role: OrgMemberRole;
815
+ expires_at: string;
816
+ }
817
+
818
+ export interface InvitationAcceptedEventData {
819
+ invitation_id: string;
820
+ org_id: string;
821
+ user_id: string;
822
+ display_name: string;
823
+ role: OrgMemberRole;
824
+ }
825
+
826
+ export interface InvitationDeclinedEventData {
827
+ invitation_id: string;
828
+ org_id: string;
829
+ email: string;
830
+ }
831
+
832
+ // ============================================================
833
+ // Platform Config Types
834
+ // ============================================================
835
+
836
+ export interface PlatformConfigResponse {
837
+ version: string;
838
+ schema_version: number;
839
+ min_plugin_version?: string;
840
+ config: Record<string, unknown>;
841
+ }
842
+
843
+ export interface AgentConfigUpdateData {
844
+ version: string;
845
+ }
package/src/ws.ts CHANGED
@@ -4,6 +4,15 @@ import type { WsFrame, WsEventMap, WsTicketResponse } from './types.js';
4
4
  export type WsEventType = keyof WsEventMap;
5
5
  export type WsEventHandler<T extends WsEventType> = (data: WsEventMap[T], seq?: number) => void;
6
6
 
7
+ export type WsClientEventMap = {
8
+ 'ping': { ts: number };
9
+ 'typing': { chat_id: string; thread_root_id: string | null; action: 'start' | 'stop' };
10
+ 'ack': { message_id: string };
11
+ 'read': { chat_id: string; last_read_id: string };
12
+ 'watch': { chat_ids: string[] };
13
+ 'agent.heartbeat': { session_id: string; telemetry?: Record<string, unknown> };
14
+ };
15
+
7
16
  export type WsConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
8
17
 
9
18
  export interface PondWsOptions {
@@ -74,7 +83,28 @@ export class PondWs {
74
83
 
75
84
  this.ws = new WebSocket(`${wsUrl}?${params.toString()}`);
76
85
 
86
+ // Force-reconnect if not connected within 15s.
87
+ // Handles edge case where Node 22 WebSocket transitions to CLOSED without
88
+ // firing onclose (e.g. TCP reset during Cloudflare rolling update).
89
+ const ws = this.ws;
90
+ const connectTimeout = setTimeout(() => {
91
+ if (this._state === 'connected' || this.intentionalClose) return;
92
+ ws.onclose = null;
93
+ ws.onopen = null;
94
+ ws.onerror = null;
95
+ try { ws.close(); } catch { /* ignore */ }
96
+ // Only reconnect if this socket is still the active one.
97
+ // If connect() was called again, a newer socket owns the lifecycle.
98
+ if (ws !== this.ws) return;
99
+ if (this.options.reconnect) {
100
+ this.scheduleReconnect();
101
+ } else {
102
+ this.setState('disconnected');
103
+ }
104
+ }, 15_000);
105
+
77
106
  this.ws.onopen = () => {
107
+ clearTimeout(connectTimeout);
78
108
  this.reconnectAttempts = 0;
79
109
  this.setState('connected');
80
110
  };
@@ -89,6 +119,7 @@ export class PondWs {
89
119
  };
90
120
 
91
121
  this.ws.onclose = () => {
122
+ clearTimeout(connectTimeout);
92
123
  this.stopHeartbeat();
93
124
  if (this.intentionalClose) {
94
125
  this.setState('disconnected');