acn-client 0.6.2 → 0.6.3

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/dist/index.d.mts CHANGED
@@ -489,6 +489,53 @@ interface ApiResponse<T> {
489
489
  error?: string;
490
490
  message?: string;
491
491
  }
492
+ /** Result of a follow or unfollow action */
493
+ interface FollowActionResponse {
494
+ follower_id: string;
495
+ followee_id: string;
496
+ /** Post-state: true after follow, false after unfollow */
497
+ following: boolean;
498
+ /** Whether this call actually mutated state (false on idempotent repeat) */
499
+ changed: boolean;
500
+ }
501
+ /** Result of a follow-status check */
502
+ interface FollowCheckResponse {
503
+ follower_id: string;
504
+ followee_id: string;
505
+ following: boolean;
506
+ }
507
+ type CommunicationPolicyMode = 'open' | 'closed' | 'manifest' | 'allowlist';
508
+ interface CommunicationPolicyResponse {
509
+ agent_id: string;
510
+ communication_policy: {
511
+ mode: CommunicationPolicyMode;
512
+ reject_reason?: string;
513
+ };
514
+ }
515
+ /** Result of an allowlist add/remove action */
516
+ interface AllowlistActionResponse {
517
+ /** The allowlist owner's agent ID */
518
+ owner_id: string;
519
+ target_id: string;
520
+ /** Post-state: true after add, false after remove */
521
+ allowlisted: boolean;
522
+ /** Whether this call actually mutated state */
523
+ changed: boolean;
524
+ }
525
+ /** Single allowlist entry (as returned by GET listing) */
526
+ interface AllowlistEntry {
527
+ target_id: string;
528
+ reason?: string;
529
+ /** ISO-8601 UTC timestamp of when the entry was added */
530
+ created_at: string;
531
+ }
532
+ /** GET /allowlist response */
533
+ interface AllowlistListResponse {
534
+ /** The allowlist owner's agent ID */
535
+ owner_id: string;
536
+ entries: AllowlistEntry[];
537
+ total: number;
538
+ }
492
539
 
493
540
  /**
494
541
  * ACN HTTP Client
@@ -874,13 +921,99 @@ declare class ACNClient {
874
921
  start_time?: string;
875
922
  end_time?: string;
876
923
  }): Promise<Record<string, unknown>>;
924
+ /**
925
+ * Follow another agent.
926
+ *
927
+ * Idempotent — re-following returns `changed: false`.
928
+ * @param agentId The follower (must match the authenticated agent).
929
+ * @param targetId The agent to follow.
930
+ */
931
+ follow(agentId: string, targetId: string): Promise<FollowActionResponse>;
932
+ /**
933
+ * Unfollow an agent.
934
+ *
935
+ * Idempotent — unfollowing a non-followed agent returns `changed: false`.
936
+ * @param agentId The follower (must match the authenticated agent).
937
+ * @param targetId The agent to unfollow.
938
+ */
939
+ unfollow(agentId: string, targetId: string): Promise<FollowActionResponse>;
940
+ /**
941
+ * Check whether `agentId` is following `targetId` (public endpoint).
942
+ */
943
+ checkFollow(agentId: string, targetId: string): Promise<FollowCheckResponse>;
944
+ /**
945
+ * List agents that `agentId` follows (public endpoint).
946
+ */
947
+ listFollows(agentId: string, options?: {
948
+ limit?: number;
949
+ offset?: number;
950
+ }): Promise<AgentSearchResponse>;
951
+ /**
952
+ * List agents that follow `agentId` (public endpoint).
953
+ */
954
+ listFollowers(agentId: string, options?: {
955
+ limit?: number;
956
+ offset?: number;
957
+ }): Promise<AgentSearchResponse>;
958
+ /**
959
+ * Get the authenticated agent's current communication policy (owner only).
960
+ */
961
+ getPolicy(agentId: string): Promise<CommunicationPolicyResponse>;
962
+ /**
963
+ * Update the agent's inbound communication policy (owner only).
964
+ *
965
+ * @param agentId Must match the authenticated agent.
966
+ * @param mode `open` | `closed` | `manifest` | `allowlist`
967
+ * @param rejectReason Optional message shown to rejected senders (closed mode).
968
+ */
969
+ updatePolicy(agentId: string, mode: CommunicationPolicyMode, rejectReason?: string): Promise<CommunicationPolicyResponse>;
970
+ /**
971
+ * Add an agent to the allowlist (owner only).
972
+ *
973
+ * Only effective when `communication_policy.mode = 'allowlist'`.
974
+ * Idempotent — re-adding returns `changed: false`.
975
+ *
976
+ * @param agentId Must match the authenticated agent.
977
+ * @param targetId Agent to trust.
978
+ * @param reason Optional free-form note (≤ 200 chars).
979
+ */
980
+ addToAllowlist(agentId: string, targetId: string, reason?: string): Promise<AllowlistActionResponse>;
981
+ /**
982
+ * Remove an agent from the allowlist (owner only).
983
+ *
984
+ * Idempotent — removing a non-member returns `changed: false`.
985
+ */
986
+ removeFromAllowlist(agentId: string, targetId: string): Promise<AllowlistActionResponse>;
987
+ /**
988
+ * List the authenticated agent's allowlist (owner only).
989
+ */
990
+ listAllowlist(agentId: string, options?: {
991
+ limit?: number;
992
+ offset?: number;
993
+ }): Promise<AllowlistListResponse>;
877
994
  }
878
995
  /**
879
996
  * ACN API Error
997
+ *
998
+ * Three body shapes are normalised here:
999
+ * - 4xx with string detail → `{ detail: "..." }`
1000
+ * - 422 validation (FastAPI) → `{ detail: [{loc, msg, type}, ...] }`
1001
+ * - 5xx sanitised (H4) → `{ error: "...", message: "...", request_id: "..." }`
1002
+ *
1003
+ * `errorCode` and `requestId` mirror the Python SDK's ACNError so
1004
+ * callers can branch on the error code and quote request_id in support
1005
+ * tickets without extra parsing.
880
1006
  */
881
1007
  declare class ACNError extends Error {
882
1008
  status: number;
883
- constructor(status: number, message: string);
1009
+ /** ACN internal error code (present on sanitised 5xx responses) */
1010
+ errorCode?: string;
1011
+ /** Request ID minted by ACN for 5xx responses (useful for support) */
1012
+ requestId?: string;
1013
+ constructor(status: number, message: string, options?: {
1014
+ errorCode?: string;
1015
+ requestId?: string;
1016
+ });
884
1017
  }
885
1018
 
886
1019
  /**
@@ -973,4 +1106,4 @@ declare class ACNRealtime {
973
1106
  */
974
1107
  declare function subscribeToACN<T = unknown>(baseUrl: string, channel: string, handler: WSEventHandler<T>): () => void;
975
1108
 
976
- export { ACNClient, type ACNClientOptions, ACNError, ACNRealtime, type ActivityEntry, type AgentActivity, type AgentAnalytics, type AgentInfo, type AgentJoinRequest, type AgentJoinResponse, type AgentRegisterRequest, type AgentRegisterResponse, type AgentSearchOptions, type AgentSearchResponse, type AgentStatus, type ApiResponse, type AttentionFee, type AuditEvent, type AuditQueryOptions, type BroadcastBySkillRequest, type BroadcastByTagRequest, type BroadcastRequest, type BroadcastStrategy, type ComponentHealth, type DashboardData, type ManifestContentResponse, type ManifestEntry, type ManifestListResponse, type Message, type MessageType, type MetricsData, type PaymentCapability, type PaymentDiscoveryOptions, type PaymentMethod, type PaymentNetwork, type PaymentStats, type PaymentTask, type PaymentTaskStatus, type SendMessageRequest, type SendMessageResponse, type SubnetCreateRequest, type SubnetCreateResponse, type SubnetInfo, type SystemHealth, type WSConnectionOptions, type WSEventHandler, type WSEventType, type WSMessage, type WSState, subscribeToACN };
1109
+ export { ACNClient, type ACNClientOptions, ACNError, ACNRealtime, type ActivityEntry, type AgentActivity, type AgentAnalytics, type AgentInfo, type AgentJoinRequest, type AgentJoinResponse, type AgentRegisterRequest, type AgentRegisterResponse, type AgentSearchOptions, type AgentSearchResponse, type AgentStatus, type AllowlistActionResponse, type AllowlistEntry, type AllowlistListResponse, type ApiResponse, type AttentionFee, type AuditEvent, type AuditQueryOptions, type BroadcastBySkillRequest, type BroadcastByTagRequest, type BroadcastRequest, type BroadcastStrategy, type CommunicationPolicyMode, type CommunicationPolicyResponse, type ComponentHealth, type DashboardData, type FollowActionResponse, type FollowCheckResponse, type ManifestContentResponse, type ManifestEntry, type ManifestListResponse, type Message, type MessageType, type MetricsData, type PaymentCapability, type PaymentDiscoveryOptions, type PaymentMethod, type PaymentNetwork, type PaymentStats, type PaymentTask, type PaymentTaskStatus, type SendMessageRequest, type SendMessageResponse, type SubnetCreateRequest, type SubnetCreateResponse, type SubnetInfo, type SystemHealth, type WSConnectionOptions, type WSEventHandler, type WSEventType, type WSMessage, type WSState, subscribeToACN };
package/dist/index.d.ts CHANGED
@@ -489,6 +489,53 @@ interface ApiResponse<T> {
489
489
  error?: string;
490
490
  message?: string;
491
491
  }
492
+ /** Result of a follow or unfollow action */
493
+ interface FollowActionResponse {
494
+ follower_id: string;
495
+ followee_id: string;
496
+ /** Post-state: true after follow, false after unfollow */
497
+ following: boolean;
498
+ /** Whether this call actually mutated state (false on idempotent repeat) */
499
+ changed: boolean;
500
+ }
501
+ /** Result of a follow-status check */
502
+ interface FollowCheckResponse {
503
+ follower_id: string;
504
+ followee_id: string;
505
+ following: boolean;
506
+ }
507
+ type CommunicationPolicyMode = 'open' | 'closed' | 'manifest' | 'allowlist';
508
+ interface CommunicationPolicyResponse {
509
+ agent_id: string;
510
+ communication_policy: {
511
+ mode: CommunicationPolicyMode;
512
+ reject_reason?: string;
513
+ };
514
+ }
515
+ /** Result of an allowlist add/remove action */
516
+ interface AllowlistActionResponse {
517
+ /** The allowlist owner's agent ID */
518
+ owner_id: string;
519
+ target_id: string;
520
+ /** Post-state: true after add, false after remove */
521
+ allowlisted: boolean;
522
+ /** Whether this call actually mutated state */
523
+ changed: boolean;
524
+ }
525
+ /** Single allowlist entry (as returned by GET listing) */
526
+ interface AllowlistEntry {
527
+ target_id: string;
528
+ reason?: string;
529
+ /** ISO-8601 UTC timestamp of when the entry was added */
530
+ created_at: string;
531
+ }
532
+ /** GET /allowlist response */
533
+ interface AllowlistListResponse {
534
+ /** The allowlist owner's agent ID */
535
+ owner_id: string;
536
+ entries: AllowlistEntry[];
537
+ total: number;
538
+ }
492
539
 
493
540
  /**
494
541
  * ACN HTTP Client
@@ -874,13 +921,99 @@ declare class ACNClient {
874
921
  start_time?: string;
875
922
  end_time?: string;
876
923
  }): Promise<Record<string, unknown>>;
924
+ /**
925
+ * Follow another agent.
926
+ *
927
+ * Idempotent — re-following returns `changed: false`.
928
+ * @param agentId The follower (must match the authenticated agent).
929
+ * @param targetId The agent to follow.
930
+ */
931
+ follow(agentId: string, targetId: string): Promise<FollowActionResponse>;
932
+ /**
933
+ * Unfollow an agent.
934
+ *
935
+ * Idempotent — unfollowing a non-followed agent returns `changed: false`.
936
+ * @param agentId The follower (must match the authenticated agent).
937
+ * @param targetId The agent to unfollow.
938
+ */
939
+ unfollow(agentId: string, targetId: string): Promise<FollowActionResponse>;
940
+ /**
941
+ * Check whether `agentId` is following `targetId` (public endpoint).
942
+ */
943
+ checkFollow(agentId: string, targetId: string): Promise<FollowCheckResponse>;
944
+ /**
945
+ * List agents that `agentId` follows (public endpoint).
946
+ */
947
+ listFollows(agentId: string, options?: {
948
+ limit?: number;
949
+ offset?: number;
950
+ }): Promise<AgentSearchResponse>;
951
+ /**
952
+ * List agents that follow `agentId` (public endpoint).
953
+ */
954
+ listFollowers(agentId: string, options?: {
955
+ limit?: number;
956
+ offset?: number;
957
+ }): Promise<AgentSearchResponse>;
958
+ /**
959
+ * Get the authenticated agent's current communication policy (owner only).
960
+ */
961
+ getPolicy(agentId: string): Promise<CommunicationPolicyResponse>;
962
+ /**
963
+ * Update the agent's inbound communication policy (owner only).
964
+ *
965
+ * @param agentId Must match the authenticated agent.
966
+ * @param mode `open` | `closed` | `manifest` | `allowlist`
967
+ * @param rejectReason Optional message shown to rejected senders (closed mode).
968
+ */
969
+ updatePolicy(agentId: string, mode: CommunicationPolicyMode, rejectReason?: string): Promise<CommunicationPolicyResponse>;
970
+ /**
971
+ * Add an agent to the allowlist (owner only).
972
+ *
973
+ * Only effective when `communication_policy.mode = 'allowlist'`.
974
+ * Idempotent — re-adding returns `changed: false`.
975
+ *
976
+ * @param agentId Must match the authenticated agent.
977
+ * @param targetId Agent to trust.
978
+ * @param reason Optional free-form note (≤ 200 chars).
979
+ */
980
+ addToAllowlist(agentId: string, targetId: string, reason?: string): Promise<AllowlistActionResponse>;
981
+ /**
982
+ * Remove an agent from the allowlist (owner only).
983
+ *
984
+ * Idempotent — removing a non-member returns `changed: false`.
985
+ */
986
+ removeFromAllowlist(agentId: string, targetId: string): Promise<AllowlistActionResponse>;
987
+ /**
988
+ * List the authenticated agent's allowlist (owner only).
989
+ */
990
+ listAllowlist(agentId: string, options?: {
991
+ limit?: number;
992
+ offset?: number;
993
+ }): Promise<AllowlistListResponse>;
877
994
  }
878
995
  /**
879
996
  * ACN API Error
997
+ *
998
+ * Three body shapes are normalised here:
999
+ * - 4xx with string detail → `{ detail: "..." }`
1000
+ * - 422 validation (FastAPI) → `{ detail: [{loc, msg, type}, ...] }`
1001
+ * - 5xx sanitised (H4) → `{ error: "...", message: "...", request_id: "..." }`
1002
+ *
1003
+ * `errorCode` and `requestId` mirror the Python SDK's ACNError so
1004
+ * callers can branch on the error code and quote request_id in support
1005
+ * tickets without extra parsing.
880
1006
  */
881
1007
  declare class ACNError extends Error {
882
1008
  status: number;
883
- constructor(status: number, message: string);
1009
+ /** ACN internal error code (present on sanitised 5xx responses) */
1010
+ errorCode?: string;
1011
+ /** Request ID minted by ACN for 5xx responses (useful for support) */
1012
+ requestId?: string;
1013
+ constructor(status: number, message: string, options?: {
1014
+ errorCode?: string;
1015
+ requestId?: string;
1016
+ });
884
1017
  }
885
1018
 
886
1019
  /**
@@ -973,4 +1106,4 @@ declare class ACNRealtime {
973
1106
  */
974
1107
  declare function subscribeToACN<T = unknown>(baseUrl: string, channel: string, handler: WSEventHandler<T>): () => void;
975
1108
 
976
- export { ACNClient, type ACNClientOptions, ACNError, ACNRealtime, type ActivityEntry, type AgentActivity, type AgentAnalytics, type AgentInfo, type AgentJoinRequest, type AgentJoinResponse, type AgentRegisterRequest, type AgentRegisterResponse, type AgentSearchOptions, type AgentSearchResponse, type AgentStatus, type ApiResponse, type AttentionFee, type AuditEvent, type AuditQueryOptions, type BroadcastBySkillRequest, type BroadcastByTagRequest, type BroadcastRequest, type BroadcastStrategy, type ComponentHealth, type DashboardData, type ManifestContentResponse, type ManifestEntry, type ManifestListResponse, type Message, type MessageType, type MetricsData, type PaymentCapability, type PaymentDiscoveryOptions, type PaymentMethod, type PaymentNetwork, type PaymentStats, type PaymentTask, type PaymentTaskStatus, type SendMessageRequest, type SendMessageResponse, type SubnetCreateRequest, type SubnetCreateResponse, type SubnetInfo, type SystemHealth, type WSConnectionOptions, type WSEventHandler, type WSEventType, type WSMessage, type WSState, subscribeToACN };
1109
+ export { ACNClient, type ACNClientOptions, ACNError, ACNRealtime, type ActivityEntry, type AgentActivity, type AgentAnalytics, type AgentInfo, type AgentJoinRequest, type AgentJoinResponse, type AgentRegisterRequest, type AgentRegisterResponse, type AgentSearchOptions, type AgentSearchResponse, type AgentStatus, type AllowlistActionResponse, type AllowlistEntry, type AllowlistListResponse, type ApiResponse, type AttentionFee, type AuditEvent, type AuditQueryOptions, type BroadcastBySkillRequest, type BroadcastByTagRequest, type BroadcastRequest, type BroadcastStrategy, type CommunicationPolicyMode, type CommunicationPolicyResponse, type ComponentHealth, type DashboardData, type FollowActionResponse, type FollowCheckResponse, type ManifestContentResponse, type ManifestEntry, type ManifestListResponse, type Message, type MessageType, type MetricsData, type PaymentCapability, type PaymentDiscoveryOptions, type PaymentMethod, type PaymentNetwork, type PaymentStats, type PaymentTask, type PaymentTaskStatus, type SendMessageRequest, type SendMessageResponse, type SubnetCreateRequest, type SubnetCreateResponse, type SubnetInfo, type SystemHealth, type WSConnectionOptions, type WSEventHandler, type WSEventType, type WSMessage, type WSState, subscribeToACN };
package/dist/index.js CHANGED
@@ -81,8 +81,32 @@ var ACNClient = class {
81
81
  signal: controller.signal
82
82
  });
83
83
  if (!response.ok) {
84
- const error = await response.json().catch(() => ({ message: response.statusText }));
85
- throw new ACNError(response.status, error.detail || error.message || "Request failed");
84
+ let body = {};
85
+ try {
86
+ const parsed = await response.json();
87
+ if (parsed && typeof parsed === "object") body = parsed;
88
+ } catch {
89
+ }
90
+ let message;
91
+ const rawDetail = body.detail;
92
+ if (typeof rawDetail === "string") {
93
+ message = rawDetail;
94
+ } else if (Array.isArray(rawDetail) && rawDetail.length > 0) {
95
+ message = rawDetail.slice(0, 5).map((item) => {
96
+ if (item && typeof item === "object") {
97
+ const i = item;
98
+ const loc = Array.isArray(i.loc) ? i.loc.slice(1).join(".") : "";
99
+ const msg = String(i.msg ?? i.type ?? item);
100
+ return loc ? `${loc}: ${msg}` : msg;
101
+ }
102
+ return String(item);
103
+ }).join("; ");
104
+ } else {
105
+ message = String(body.message ?? response.statusText ?? `HTTP ${response.status}`);
106
+ }
107
+ const errorCode = typeof body.error === "string" ? body.error : void 0;
108
+ const requestId = typeof body.request_id === "string" ? body.request_id : response.headers.get("X-Request-ID") ?? void 0;
109
+ throw new ACNError(response.status, message, { errorCode, requestId });
86
110
  }
87
111
  if (response.status === 204) {
88
112
  return void 0;
@@ -622,13 +646,116 @@ var ACNClient = class {
622
646
  async getAuditStats(options) {
623
647
  return this.get("/api/v1/audit/stats", options);
624
648
  }
649
+ // ============================================
650
+ // Social Graph (Follow)
651
+ // ============================================
652
+ /**
653
+ * Follow another agent.
654
+ *
655
+ * Idempotent — re-following returns `changed: false`.
656
+ * @param agentId The follower (must match the authenticated agent).
657
+ * @param targetId The agent to follow.
658
+ */
659
+ async follow(agentId, targetId) {
660
+ return this.post(`/api/v1/agents/${agentId}/follows/${targetId}`);
661
+ }
662
+ /**
663
+ * Unfollow an agent.
664
+ *
665
+ * Idempotent — unfollowing a non-followed agent returns `changed: false`.
666
+ * @param agentId The follower (must match the authenticated agent).
667
+ * @param targetId The agent to unfollow.
668
+ */
669
+ async unfollow(agentId, targetId) {
670
+ return this.delete(`/api/v1/agents/${agentId}/follows/${targetId}`);
671
+ }
672
+ /**
673
+ * Check whether `agentId` is following `targetId` (public endpoint).
674
+ */
675
+ async checkFollow(agentId, targetId) {
676
+ return this.get(`/api/v1/agents/${agentId}/follows/${targetId}`);
677
+ }
678
+ /**
679
+ * List agents that `agentId` follows (public endpoint).
680
+ */
681
+ async listFollows(agentId, options) {
682
+ return this.get(`/api/v1/agents/${agentId}/follows`, options);
683
+ }
684
+ /**
685
+ * List agents that follow `agentId` (public endpoint).
686
+ */
687
+ async listFollowers(agentId, options) {
688
+ return this.get(`/api/v1/agents/${agentId}/followers`, options);
689
+ }
690
+ // ============================================
691
+ // Communication Policy
692
+ // ============================================
693
+ /**
694
+ * Get the authenticated agent's current communication policy (owner only).
695
+ */
696
+ async getPolicy(agentId) {
697
+ return this.get(`/api/v1/agents/${agentId}/policy`);
698
+ }
699
+ /**
700
+ * Update the agent's inbound communication policy (owner only).
701
+ *
702
+ * @param agentId Must match the authenticated agent.
703
+ * @param mode `open` | `closed` | `manifest` | `allowlist`
704
+ * @param rejectReason Optional message shown to rejected senders (closed mode).
705
+ */
706
+ async updatePolicy(agentId, mode, rejectReason) {
707
+ const policy = { mode };
708
+ if (rejectReason !== void 0) policy.reject_reason = rejectReason;
709
+ return this.request("PATCH", `/api/v1/agents/${agentId}/policy`, {
710
+ body: { communication_policy: policy }
711
+ });
712
+ }
713
+ // ============================================
714
+ // Allowlist
715
+ // ============================================
716
+ /**
717
+ * Add an agent to the allowlist (owner only).
718
+ *
719
+ * Only effective when `communication_policy.mode = 'allowlist'`.
720
+ * Idempotent — re-adding returns `changed: false`.
721
+ *
722
+ * @param agentId Must match the authenticated agent.
723
+ * @param targetId Agent to trust.
724
+ * @param reason Optional free-form note (≤ 200 chars).
725
+ */
726
+ async addToAllowlist(agentId, targetId, reason) {
727
+ return this.post(
728
+ `/api/v1/agents/${agentId}/allowlist/${targetId}`,
729
+ reason !== void 0 ? { reason } : void 0
730
+ );
731
+ }
732
+ /**
733
+ * Remove an agent from the allowlist (owner only).
734
+ *
735
+ * Idempotent — removing a non-member returns `changed: false`.
736
+ */
737
+ async removeFromAllowlist(agentId, targetId) {
738
+ return this.delete(`/api/v1/agents/${agentId}/allowlist/${targetId}`);
739
+ }
740
+ /**
741
+ * List the authenticated agent's allowlist (owner only).
742
+ */
743
+ async listAllowlist(agentId, options) {
744
+ return this.get(`/api/v1/agents/${agentId}/allowlist`, options);
745
+ }
625
746
  };
626
747
  var ACNError = class extends Error {
627
- constructor(status, message) {
748
+ constructor(status, message, options) {
628
749
  super(message);
629
750
  this.status = status;
630
751
  this.name = "ACNError";
752
+ this.errorCode = options?.errorCode;
753
+ this.requestId = options?.requestId;
631
754
  }
755
+ /** ACN internal error code (present on sanitised 5xx responses) */
756
+ errorCode;
757
+ /** Request ID minted by ACN for 5xx responses (useful for support) */
758
+ requestId;
632
759
  };
633
760
 
634
761
  // src/realtime.ts
package/dist/index.mjs CHANGED
@@ -42,8 +42,32 @@ var ACNClient = class {
42
42
  signal: controller.signal
43
43
  });
44
44
  if (!response.ok) {
45
- const error = await response.json().catch(() => ({ message: response.statusText }));
46
- throw new ACNError(response.status, error.detail || error.message || "Request failed");
45
+ let body = {};
46
+ try {
47
+ const parsed = await response.json();
48
+ if (parsed && typeof parsed === "object") body = parsed;
49
+ } catch {
50
+ }
51
+ let message;
52
+ const rawDetail = body.detail;
53
+ if (typeof rawDetail === "string") {
54
+ message = rawDetail;
55
+ } else if (Array.isArray(rawDetail) && rawDetail.length > 0) {
56
+ message = rawDetail.slice(0, 5).map((item) => {
57
+ if (item && typeof item === "object") {
58
+ const i = item;
59
+ const loc = Array.isArray(i.loc) ? i.loc.slice(1).join(".") : "";
60
+ const msg = String(i.msg ?? i.type ?? item);
61
+ return loc ? `${loc}: ${msg}` : msg;
62
+ }
63
+ return String(item);
64
+ }).join("; ");
65
+ } else {
66
+ message = String(body.message ?? response.statusText ?? `HTTP ${response.status}`);
67
+ }
68
+ const errorCode = typeof body.error === "string" ? body.error : void 0;
69
+ const requestId = typeof body.request_id === "string" ? body.request_id : response.headers.get("X-Request-ID") ?? void 0;
70
+ throw new ACNError(response.status, message, { errorCode, requestId });
47
71
  }
48
72
  if (response.status === 204) {
49
73
  return void 0;
@@ -583,13 +607,116 @@ var ACNClient = class {
583
607
  async getAuditStats(options) {
584
608
  return this.get("/api/v1/audit/stats", options);
585
609
  }
610
+ // ============================================
611
+ // Social Graph (Follow)
612
+ // ============================================
613
+ /**
614
+ * Follow another agent.
615
+ *
616
+ * Idempotent — re-following returns `changed: false`.
617
+ * @param agentId The follower (must match the authenticated agent).
618
+ * @param targetId The agent to follow.
619
+ */
620
+ async follow(agentId, targetId) {
621
+ return this.post(`/api/v1/agents/${agentId}/follows/${targetId}`);
622
+ }
623
+ /**
624
+ * Unfollow an agent.
625
+ *
626
+ * Idempotent — unfollowing a non-followed agent returns `changed: false`.
627
+ * @param agentId The follower (must match the authenticated agent).
628
+ * @param targetId The agent to unfollow.
629
+ */
630
+ async unfollow(agentId, targetId) {
631
+ return this.delete(`/api/v1/agents/${agentId}/follows/${targetId}`);
632
+ }
633
+ /**
634
+ * Check whether `agentId` is following `targetId` (public endpoint).
635
+ */
636
+ async checkFollow(agentId, targetId) {
637
+ return this.get(`/api/v1/agents/${agentId}/follows/${targetId}`);
638
+ }
639
+ /**
640
+ * List agents that `agentId` follows (public endpoint).
641
+ */
642
+ async listFollows(agentId, options) {
643
+ return this.get(`/api/v1/agents/${agentId}/follows`, options);
644
+ }
645
+ /**
646
+ * List agents that follow `agentId` (public endpoint).
647
+ */
648
+ async listFollowers(agentId, options) {
649
+ return this.get(`/api/v1/agents/${agentId}/followers`, options);
650
+ }
651
+ // ============================================
652
+ // Communication Policy
653
+ // ============================================
654
+ /**
655
+ * Get the authenticated agent's current communication policy (owner only).
656
+ */
657
+ async getPolicy(agentId) {
658
+ return this.get(`/api/v1/agents/${agentId}/policy`);
659
+ }
660
+ /**
661
+ * Update the agent's inbound communication policy (owner only).
662
+ *
663
+ * @param agentId Must match the authenticated agent.
664
+ * @param mode `open` | `closed` | `manifest` | `allowlist`
665
+ * @param rejectReason Optional message shown to rejected senders (closed mode).
666
+ */
667
+ async updatePolicy(agentId, mode, rejectReason) {
668
+ const policy = { mode };
669
+ if (rejectReason !== void 0) policy.reject_reason = rejectReason;
670
+ return this.request("PATCH", `/api/v1/agents/${agentId}/policy`, {
671
+ body: { communication_policy: policy }
672
+ });
673
+ }
674
+ // ============================================
675
+ // Allowlist
676
+ // ============================================
677
+ /**
678
+ * Add an agent to the allowlist (owner only).
679
+ *
680
+ * Only effective when `communication_policy.mode = 'allowlist'`.
681
+ * Idempotent — re-adding returns `changed: false`.
682
+ *
683
+ * @param agentId Must match the authenticated agent.
684
+ * @param targetId Agent to trust.
685
+ * @param reason Optional free-form note (≤ 200 chars).
686
+ */
687
+ async addToAllowlist(agentId, targetId, reason) {
688
+ return this.post(
689
+ `/api/v1/agents/${agentId}/allowlist/${targetId}`,
690
+ reason !== void 0 ? { reason } : void 0
691
+ );
692
+ }
693
+ /**
694
+ * Remove an agent from the allowlist (owner only).
695
+ *
696
+ * Idempotent — removing a non-member returns `changed: false`.
697
+ */
698
+ async removeFromAllowlist(agentId, targetId) {
699
+ return this.delete(`/api/v1/agents/${agentId}/allowlist/${targetId}`);
700
+ }
701
+ /**
702
+ * List the authenticated agent's allowlist (owner only).
703
+ */
704
+ async listAllowlist(agentId, options) {
705
+ return this.get(`/api/v1/agents/${agentId}/allowlist`, options);
706
+ }
586
707
  };
587
708
  var ACNError = class extends Error {
588
- constructor(status, message) {
709
+ constructor(status, message, options) {
589
710
  super(message);
590
711
  this.status = status;
591
712
  this.name = "ACNError";
713
+ this.errorCode = options?.errorCode;
714
+ this.requestId = options?.requestId;
592
715
  }
716
+ /** ACN internal error code (present on sanitised 5xx responses) */
717
+ errorCode;
718
+ /** Request ID minted by ACN for 5xx responses (useful for support) */
719
+ requestId;
593
720
  };
594
721
 
595
722
  // src/realtime.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acn-client",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Official TypeScript/JavaScript client for ACN (Agent Collaboration Network)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",