acp-ts 1.1.6 → 1.1.8

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/agentcp.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { IAgentCP, IAgentIdentity } from "./interfaces";
2
2
  import { MessageStore } from "./messagestore";
3
3
  import { AgentMdOptions } from "./agentmd";
4
- import { ACPGroupClient, GroupOperations, ACPGroupEventHandler, CursorStore, GroupMessageStore, GroupMessage } from "./group";
4
+ import { ACPGroupClient, GroupOperations, ACPGroupEventHandler, CursorStore, GroupMessageStore, GroupMessage, MsgCursor } from "./group";
5
5
  declare class AgentCP implements IAgentCP {
6
6
  private seedPassword;
7
7
  private apUrl;
@@ -19,6 +19,9 @@ declare class AgentCP implements IAgentCP {
19
19
  private _groupTargetAid;
20
20
  private _groupSessionId;
21
21
  private _persistGroupMessages;
22
+ private _onlineGroups;
23
+ private _heartbeatTimer;
24
+ private _heartbeatIntervalMs;
22
25
  get messageStore(): MessageStore;
23
26
  constructor(apiUrl: string, seedPassword?: string, basePath?: string, options?: {
24
27
  persistMessages?: boolean;
@@ -86,16 +89,23 @@ declare class AgentCP implements IAgentCP {
86
89
  */
87
90
  setGroupEventHandler(handler: ACPGroupEventHandler): void;
88
91
  /**
89
- * 创建默认的群组事件处理器(防御性,仅打印日志)。
92
+ * 创建默认的群组事件处理器。
93
+ * - onGroupMessage: 自动存储消息到 GroupMessageStore + 自动 ACK
94
+ * - onJoinApproved: 自动注册到 Home AP + 存储群组
90
95
  * 外部可通过 setGroupEventHandler 覆盖。
91
96
  */
92
97
  private _createDefaultGroupEventHandler;
98
+ /**
99
+ * 自动 ACK 消息(SDK 内部行为,对上层透明)
100
+ */
101
+ private _autoAckMessage;
93
102
  /**
94
103
  * 设置群组游标存储
95
104
  */
96
105
  setGroupCursorStore(store: CursorStore): void;
97
106
  /**
98
- * 关闭群组客户端
107
+ * 关闭群组客户端。
108
+ * 自动停止心跳、清理在线群组状态。
99
109
  */
100
110
  closeGroupClient(): void;
101
111
  /**
@@ -132,6 +142,10 @@ declare class AgentCP implements IAgentCP {
132
142
  * 添加群组到本地存储
133
143
  */
134
144
  addGroupToStore(groupId: string, name: string): void;
145
+ /**
146
+ * 将加入的群组注册到 Home AP(内部方法)
147
+ */
148
+ registerGroupToHomeAP(groupId: string, groupUrl: string, role?: string): Promise<void>;
135
149
  /**
136
150
  * 从本地存储删除群组
137
151
  */
@@ -154,9 +168,54 @@ declare class AgentCP implements IAgentCP {
154
168
  getGroupLastMsgId(groupId: string): number;
155
169
  /**
156
170
  * 从服务端拉取新消息并同步到本地存储。
171
+ * 循环拉取直到 has_more=false,每批拉取后自动 ACK。
157
172
  * 返回所有本地缓存的消息(包括新拉取的)。
173
+ *
174
+ * @param groupId 群组 ID
175
+ * @param afterMsgId 从哪条消息之后开始拉取,0 表示使用服务端自动游标模式
176
+ * @param limit 每次拉取数量上限,0 表示使用服务端默认值
177
+ */
178
+ pullAndStoreGroupMessages(groupId: string, afterMsgId?: number, limit?: number): Promise<GroupMessage[]>;
179
+ /**
180
+ * 加入群组会话(完整生命周期):
181
+ * 1. register_online → 获取游标状态
182
+ * 2. 如果有未读消息 → 循环 pull_messages + ACK
183
+ * 3. 启动心跳定时器(首次时启动)
184
+ *
185
+ * @returns msg_cursor 游标状态
186
+ */
187
+ joinGroupSession(groupId: string): Promise<MsgCursor>;
188
+ /**
189
+ * 离开群组会话(优雅退出):
190
+ * 1. unregister_online
191
+ * 2. 从在线群组列表移除
192
+ * 3. 如果没有在线群组了,停止心跳定时器
193
+ */
194
+ leaveGroupSession(groupId: string): Promise<void>;
195
+ /**
196
+ * 离开所有群组会话
197
+ */
198
+ leaveAllGroupSessions(): Promise<void>;
199
+ /**
200
+ * 获取当前在线群组列表
201
+ */
202
+ getOnlineGroups(): string[];
203
+ /**
204
+ * 设置心跳间隔(毫秒),默认 180000(3 分钟)
205
+ */
206
+ setHeartbeatInterval(intervalMs: number): void;
207
+ /**
208
+ * 确保心跳定时器已启动
209
+ */
210
+ private _ensureHeartbeat;
211
+ /**
212
+ * 停止心跳定时器
213
+ */
214
+ private _stopHeartbeat;
215
+ /**
216
+ * 对所有在线群组发送心跳
158
217
  */
159
- pullAndStoreGroupMessages(groupId: string, limit?: number): Promise<GroupMessage[]>;
218
+ private _sendHeartbeats;
160
219
  /**
161
220
  * 关闭群消息存储,刷新所有未写入的数据
162
221
  */
package/dist/agentcp.js CHANGED
@@ -62,6 +62,10 @@ class AgentCP {
62
62
  this._groupTargetAid = '';
63
63
  this._groupSessionId = '';
64
64
  this._persistGroupMessages = false;
65
+ // Group lifecycle management
66
+ this._onlineGroups = new Set();
67
+ this._heartbeatTimer = null;
68
+ this._heartbeatIntervalMs = 180000; // 3 minutes
65
69
  if (!apiUrl) {
66
70
  this.handleError("参数缺失:apiUrl不应为空");
67
71
  }
@@ -369,11 +373,9 @@ class AgentCP {
369
373
  }
370
374
  const data = message.data || message;
371
375
  const sender = (_a = data.sender) !== null && _a !== void 0 ? _a : "";
372
- // console.log(`[Group] handleGroupMessage: sender="${sender}" targetAid="${this._groupTargetAid}" match=${sender === this._groupTargetAid}`);
373
376
  if (sender === this._groupTargetAid) {
374
377
  try {
375
378
  const rawMsg = (_b = data.message) !== null && _b !== void 0 ? _b : "";
376
- // console.log(`[Group] rawMsg type=${typeof rawMsg}, length=${typeof rawMsg === 'string' ? rawMsg.length : 'N/A'}, first 300 chars: ${typeof rawMsg === 'string' ? rawMsg.substring(0, 300) : JSON.stringify(rawMsg).substring(0, 300)}`);
377
379
  if (typeof rawMsg === 'string' && rawMsg) {
378
380
  this.groupClient.handleIncoming(rawMsg);
379
381
  }
@@ -398,7 +400,9 @@ class AgentCP {
398
400
  }
399
401
  }
400
402
  /**
401
- * 创建默认的群组事件处理器(防御性,仅打印日志)。
403
+ * 创建默认的群组事件处理器。
404
+ * - onGroupMessage: 自动存储消息到 GroupMessageStore + 自动 ACK
405
+ * - onJoinApproved: 自动注册到 Home AP + 存储群组
402
406
  * 外部可通过 setGroupEventHandler 覆盖。
403
407
  */
404
408
  _createDefaultGroupEventHandler() {
@@ -412,8 +416,28 @@ class AgentCP {
412
416
  onGroupInvite(groupId, groupAddress, invitedBy) {
413
417
  console.log(`[Group][DefaultHandler] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
414
418
  },
415
- onJoinApproved(groupId, groupAddress) {
419
+ onJoinApproved: (groupId, groupAddress) => {
416
420
  console.log(`[Group][DefaultHandler] onJoinApproved: group=${groupId} address=${groupAddress}`);
421
+ (async () => {
422
+ try {
423
+ if (!this.groupOps || !this._groupTargetAid) {
424
+ console.warn(`[Group][DefaultHandler] onJoinApproved skipped: groupOps or targetAid not available`);
425
+ return;
426
+ }
427
+ let groupName = groupId;
428
+ try {
429
+ const info = await this.groupOps.getGroupInfo(this._groupTargetAid, groupId);
430
+ groupName = info.name || groupId;
431
+ }
432
+ catch (_) { }
433
+ this.addGroupToStore(groupId, groupName);
434
+ const groupUrl = groupAddress || `https://${this._groupTargetAid}/${groupId}`;
435
+ await this.registerGroupToHomeAP(groupId, groupUrl);
436
+ }
437
+ catch (e) {
438
+ console.error(`[Group][DefaultHandler] onJoinApproved processing failed: group=${groupId}`, e.message);
439
+ }
440
+ })();
417
441
  },
418
442
  onJoinRejected(groupId, reason) {
419
443
  console.log(`[Group][DefaultHandler] onJoinRejected: group=${groupId} reason=${reason}`);
@@ -421,14 +445,28 @@ class AgentCP {
421
445
  onJoinRequestReceived(groupId, agentId, message) {
422
446
  console.log(`[Group][DefaultHandler] onJoinRequestReceived: group=${groupId} agent=${agentId}`);
423
447
  },
424
- onGroupMessage(groupId, msg) {
448
+ onGroupMessage: (groupId, msg) => {
425
449
  console.log(`[Group][DefaultHandler] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
450
+ // 自动存储消息到本地
451
+ this.addGroupMessageToStore(groupId, msg);
452
+ // 自动 ACK(异步,不阻塞回调)
453
+ this._autoAckMessage(groupId, msg.msg_id);
426
454
  },
427
455
  onGroupEvent(groupId, evt) {
428
456
  console.log(`[Group][DefaultHandler] onGroupEvent: group=${groupId} event=${evt.event_type}`);
429
457
  },
430
458
  };
431
459
  }
460
+ /**
461
+ * 自动 ACK 消息(SDK 内部行为,对上层透明)
462
+ */
463
+ _autoAckMessage(groupId, msgId) {
464
+ if (!this.groupOps || !this._groupTargetAid)
465
+ return;
466
+ this.groupOps.ackMessages(this._groupTargetAid, groupId, msgId).catch(e => {
467
+ console.warn(`[Group] auto ack failed: group=${groupId} msgId=${msgId}`, e.message || e);
468
+ });
469
+ }
432
470
  /**
433
471
  * 设置群组游标存储
434
472
  */
@@ -438,9 +476,12 @@ class AgentCP {
438
476
  }
439
477
  }
440
478
  /**
441
- * 关闭群组客户端
479
+ * 关闭群组客户端。
480
+ * 自动停止心跳、清理在线群组状态。
442
481
  */
443
482
  closeGroupClient() {
483
+ this._stopHeartbeat();
484
+ this._onlineGroups.clear();
444
485
  if (this.groupClient) {
445
486
  try {
446
487
  this.groupClient.close();
@@ -533,6 +574,20 @@ class AgentCP {
533
574
  return;
534
575
  this.groupMessageStore.getOrCreateGroup(groupId, this._groupTargetAid, name);
535
576
  }
577
+ /**
578
+ * 将加入的群组注册到 Home AP(内部方法)
579
+ */
580
+ async registerGroupToHomeAP(groupId, groupUrl, role = 'member') {
581
+ if (!this.groupOps || !this._groupTargetAid)
582
+ return;
583
+ try {
584
+ await this.groupOps.registerMembership(this._groupTargetAid, groupId, groupUrl, this._groupTargetAid, this._groupSessionId, role);
585
+ console.log(`[Group] registerGroupToHomeAP success: group=${groupId}`);
586
+ }
587
+ catch (e) {
588
+ console.error(`[Group] registerGroupToHomeAP failed: group=${groupId}`, e.message);
589
+ }
590
+ }
536
591
  /**
537
592
  * 从本地存储删除群组
538
593
  */
@@ -580,16 +635,24 @@ class AgentCP {
580
635
  }
581
636
  /**
582
637
  * 从服务端拉取新消息并同步到本地存储。
638
+ * 循环拉取直到 has_more=false,每批拉取后自动 ACK。
583
639
  * 返回所有本地缓存的消息(包括新拉取的)。
640
+ *
641
+ * @param groupId 群组 ID
642
+ * @param afterMsgId 从哪条消息之后开始拉取,0 表示使用服务端自动游标模式
643
+ * @param limit 每次拉取数量上限,0 表示使用服务端默认值
584
644
  */
585
- async pullAndStoreGroupMessages(groupId, limit = 50) {
645
+ async pullAndStoreGroupMessages(groupId, afterMsgId = 0, limit = 50) {
586
646
  if (!this.groupOps || !this._groupTargetAid) {
587
647
  throw new Error('群组客户端未初始化');
588
648
  }
589
- const lastMsgId = this.getGroupLastMsgId(groupId);
649
+ let after = afterMsgId;
590
650
  try {
591
- const pulled = await this.groupOps.pullMessages(this._groupTargetAid, groupId, lastMsgId, limit);
592
- if (pulled.messages && pulled.messages.length > 0) {
651
+ while (true) {
652
+ const pulled = await this.groupOps.pullMessages(this._groupTargetAid, groupId, after, limit);
653
+ if (!pulled.messages || pulled.messages.length === 0) {
654
+ break;
655
+ }
593
656
  const msgs = pulled.messages.map(m => {
594
657
  var _a, _b, _c, _d, _e, _f;
595
658
  return ({
@@ -602,13 +665,131 @@ class AgentCP {
602
665
  });
603
666
  });
604
667
  this.addGroupMessagesToStore(groupId, msgs);
668
+ // ACK 这批消息中的最后一条
669
+ const lastMsgId = msgs[msgs.length - 1].msg_id;
670
+ await this.groupOps.ackMessages(this._groupTargetAid, groupId, lastMsgId);
671
+ // 更新 after 用于下一轮拉取
672
+ after = lastMsgId;
673
+ if (!pulled.has_more) {
674
+ break;
675
+ }
605
676
  }
606
677
  }
607
678
  catch (e) {
608
- console.warn('[AgentCP] pullMessages error:', e.message);
679
+ console.warn('[AgentCP] pullAndStoreGroupMessages error:', e.message);
609
680
  }
610
681
  return this.getLocalGroupMessages(groupId);
611
682
  }
683
+ // ============================================================
684
+ // Group Session Lifecycle (register_online / pull / heartbeat / unregister)
685
+ // ============================================================
686
+ /**
687
+ * 加入群组会话(完整生命周期):
688
+ * 1. register_online → 获取游标状态
689
+ * 2. 如果有未读消息 → 循环 pull_messages + ACK
690
+ * 3. 启动心跳定时器(首次时启动)
691
+ *
692
+ * @returns msg_cursor 游标状态
693
+ */
694
+ async joinGroupSession(groupId) {
695
+ if (!this.groupOps || !this._groupTargetAid) {
696
+ throw new Error('群组客户端未初始化,请先调用 initGroupClient');
697
+ }
698
+ // Step 1: register_online
699
+ const result = await this.groupOps.registerOnline(this._groupTargetAid, groupId);
700
+ const cursor = result.msg_cursor;
701
+ this._onlineGroups.add(groupId);
702
+ console.log(`[Group] joinGroupSession: group=${groupId} unread=${cursor.unread_count} current=${cursor.current_msg_id} latest=${cursor.latest_msg_id}`);
703
+ // Step 2: 如果有未读消息,使用自动游标模式拉取
704
+ if (cursor.unread_count > 0) {
705
+ await this.pullAndStoreGroupMessages(groupId, 0, 0);
706
+ }
707
+ // Step 3: 启动心跳定时器(首次加入群组时启动)
708
+ this._ensureHeartbeat();
709
+ return cursor;
710
+ }
711
+ /**
712
+ * 离开群组会话(优雅退出):
713
+ * 1. unregister_online
714
+ * 2. 从在线群组列表移除
715
+ * 3. 如果没有在线群组了,停止心跳定时器
716
+ */
717
+ async leaveGroupSession(groupId) {
718
+ if (!this.groupOps || !this._groupTargetAid)
719
+ return;
720
+ try {
721
+ await this.groupOps.unregisterOnline(this._groupTargetAid, groupId);
722
+ }
723
+ catch (e) {
724
+ console.warn(`[Group] unregisterOnline failed: group=${groupId}`, e.message || e);
725
+ }
726
+ this._onlineGroups.delete(groupId);
727
+ // 如果没有在线群组了,停止心跳
728
+ if (this._onlineGroups.size === 0) {
729
+ this._stopHeartbeat();
730
+ }
731
+ }
732
+ /**
733
+ * 离开所有群组会话
734
+ */
735
+ async leaveAllGroupSessions() {
736
+ const groups = Array.from(this._onlineGroups);
737
+ for (const groupId of groups) {
738
+ await this.leaveGroupSession(groupId);
739
+ }
740
+ }
741
+ /**
742
+ * 获取当前在线群组列表
743
+ */
744
+ getOnlineGroups() {
745
+ return Array.from(this._onlineGroups);
746
+ }
747
+ /**
748
+ * 设置心跳间隔(毫秒),默认 180000(3 分钟)
749
+ */
750
+ setHeartbeatInterval(intervalMs) {
751
+ this._heartbeatIntervalMs = intervalMs;
752
+ // 如果心跳已在运行,重新启动以应用新间隔
753
+ if (this._heartbeatTimer) {
754
+ this._stopHeartbeat();
755
+ this._ensureHeartbeat();
756
+ }
757
+ }
758
+ /**
759
+ * 确保心跳定时器已启动
760
+ */
761
+ _ensureHeartbeat() {
762
+ if (this._heartbeatTimer)
763
+ return;
764
+ if (this._onlineGroups.size === 0)
765
+ return;
766
+ this._heartbeatTimer = setInterval(() => {
767
+ this._sendHeartbeats();
768
+ }, this._heartbeatIntervalMs);
769
+ console.log(`[Group] heartbeat started: interval=${this._heartbeatIntervalMs}ms`);
770
+ }
771
+ /**
772
+ * 停止心跳定时器
773
+ */
774
+ _stopHeartbeat() {
775
+ if (this._heartbeatTimer) {
776
+ clearInterval(this._heartbeatTimer);
777
+ this._heartbeatTimer = null;
778
+ console.log(`[Group] heartbeat stopped`);
779
+ }
780
+ }
781
+ /**
782
+ * 对所有在线群组发送心跳
783
+ */
784
+ _sendHeartbeats() {
785
+ if (!this.groupOps || !this._groupTargetAid)
786
+ return;
787
+ for (const groupId of this._onlineGroups) {
788
+ this.groupOps.heartbeat(this._groupTargetAid, groupId).catch(e => {
789
+ console.warn(`[Group] heartbeat failed: group=${groupId}`, e.message || e);
790
+ });
791
+ }
792
+ }
612
793
  /**
613
794
  * 关闭群消息存储,刷新所有未写入的数据
614
795
  */
@@ -74,24 +74,21 @@ class ACPGroupClient {
74
74
  * Called by the message dispatch chain in AgentCP.
75
75
  */
76
76
  handleIncoming(payload) {
77
- var _a, _b, _c;
78
- // console.log(`[GroupClient] <<< handleIncoming raw payload (first 500 chars): ${payload.substring(0, 500)}`);
77
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
79
78
  let data;
80
79
  try {
81
80
  data = JSON.parse(payload);
82
81
  }
83
82
  catch (e) {
84
- console.error(`[GroupClient] !!! JSON.parse failed for incoming payload:`, e, `payload(first 200): ${payload.substring(0, 200)}`);
83
+ console.error(`[GroupClient] JSON.parse failed for incoming payload:`, e);
85
84
  return;
86
85
  }
87
- // console.log(`[GroupClient] <<< parsed data: action=${data.action} request_id=${data.request_id} event=${data.event} code=${data.code}`);
88
86
  // Try as response (has request_id)
89
87
  const requestId = (_a = data.request_id) !== null && _a !== void 0 ? _a : "";
90
88
  if (requestId) {
91
89
  const resp = (0, types_1.parseGroupResponse)(data);
92
90
  const pending = this._pendingReqs.get(requestId);
93
91
  if (pending) {
94
- // console.log(`[GroupClient] <<< matched pending request: reqId=${requestId}, resolving`);
95
92
  clearTimeout(pending.timer);
96
93
  this._pendingReqs.delete(requestId);
97
94
  pending.resolve(resp);
@@ -119,6 +116,34 @@ class ACPGroupClient {
119
116
  }
120
117
  return;
121
118
  }
119
+ // Handle action-based push messages from group.ap (e.g. message_push)
120
+ // These have action field but no event/request_id, need to be mapped to notification events
121
+ const action = (_d = data.action) !== null && _d !== void 0 ? _d : "";
122
+ if (action === "message_push" && data.data) {
123
+ console.log(`[GroupClient] message_push -> group_message: group=${data.group_id} msg_id=${data.data.msg_id}`);
124
+ const msgData = data.data;
125
+ const notify = {
126
+ action: "group_notify",
127
+ group_id: (_e = data.group_id) !== null && _e !== void 0 ? _e : "",
128
+ event: types_1.NOTIFY_GROUP_MESSAGE,
129
+ data: {
130
+ msg_id: (_f = msgData.msg_id) !== null && _f !== void 0 ? _f : 0,
131
+ sender: (_g = msgData.sender) !== null && _g !== void 0 ? _g : "",
132
+ content: (_h = msgData.content) !== null && _h !== void 0 ? _h : "",
133
+ content_type: (_j = msgData.content_type) !== null && _j !== void 0 ? _j : "text",
134
+ timestamp: (_k = msgData.timestamp) !== null && _k !== void 0 ? _k : 0,
135
+ metadata: (_l = msgData.metadata) !== null && _l !== void 0 ? _l : null,
136
+ },
137
+ timestamp: (_m = msgData.timestamp) !== null && _m !== void 0 ? _m : 0,
138
+ };
139
+ if (this._handler != null) {
140
+ (0, events_1.dispatchAcpNotify)(this._handler, notify);
141
+ }
142
+ else {
143
+ console.warn(`[GroupClient] !!! message_push dropped: no event handler registered.`);
144
+ }
145
+ return;
146
+ }
122
147
  console.warn(`[GroupClient] !!! unhandled incoming message: no request_id and no event field`, JSON.stringify(data).substring(0, 300));
123
148
  }
124
149
  // -- Lifecycle --
@@ -2,7 +2,7 @@
2
2
  * ACP Group Operations package.
3
3
  * Mirrors Python SDK: agentcp/group/__init__.py
4
4
  */
5
- export { GroupErrorCode, GroupError, GroupRequest, GroupResponse, GroupNotify, buildGroupRequest, groupRequestToJson, parseGroupResponse, parseGroupNotify, GroupMessage, GroupEvent, MsgCursor, EventCursor, CursorState, createMsgCursor, createEventCursor, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, MembershipInfo, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, NOTIFY_NEW_MESSAGE, NOTIFY_NEW_EVENT, NOTIFY_GROUP_INVITE, NOTIFY_JOIN_APPROVED, NOTIFY_JOIN_REJECTED, NOTIFY_JOIN_REQUEST_RECEIVED, NOTIFY_GROUP_MESSAGE, NOTIFY_GROUP_EVENT, EVENT_MEMBER_JOINED, EVENT_MEMBER_REMOVED, EVENT_MEMBER_LEFT, EVENT_MEMBER_BANNED, EVENT_META_UPDATED, EVENT_RULES_UPDATED, EVENT_ANNOUNCEMENT_UPDATED, EVENT_GROUP_DISSOLVED, EVENT_MASTER_TRANSFERRED, EVENT_GROUP_SUSPENDED, EVENT_GROUP_RESUMED, EVENT_MEMBER_UNBANNED, EVENT_JOIN_REQUIREMENTS_UPDATED, EVENT_INVITE_CODE_CREATED, EVENT_INVITE_CODE_REVOKED, } from './types';
5
+ export { GroupErrorCode, GroupError, GroupRequest, GroupResponse, GroupNotify, buildGroupRequest, groupRequestToJson, parseGroupResponse, parseGroupNotify, GroupMessage, GroupEvent, MsgCursor, EventCursor, CursorState, createMsgCursor, createEventCursor, RegisterOnlineResp, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, MembershipInfo, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, NOTIFY_NEW_MESSAGE, NOTIFY_NEW_EVENT, NOTIFY_GROUP_INVITE, NOTIFY_JOIN_APPROVED, NOTIFY_JOIN_REJECTED, NOTIFY_JOIN_REQUEST_RECEIVED, NOTIFY_GROUP_MESSAGE, NOTIFY_GROUP_EVENT, EVENT_MEMBER_JOINED, EVENT_MEMBER_REMOVED, EVENT_MEMBER_LEFT, EVENT_MEMBER_BANNED, EVENT_META_UPDATED, EVENT_RULES_UPDATED, EVENT_ANNOUNCEMENT_UPDATED, EVENT_GROUP_DISSOLVED, EVENT_MASTER_TRANSFERRED, EVENT_GROUP_SUSPENDED, EVENT_GROUP_RESUMED, EVENT_MEMBER_UNBANNED, EVENT_JOIN_REQUIREMENTS_UPDATED, EVENT_INVITE_CODE_CREATED, EVENT_INVITE_CODE_REVOKED, } from './types';
6
6
  export { ACPGroupClient, SendFunc } from './client';
7
7
  export { GroupOperations, SyncHandler } from './operations';
8
8
  export { ACPGroupEventHandler, EventProcessor, dispatchAcpNotify, dispatchEvent, } from './events';
@@ -3,7 +3,7 @@
3
3
  * Mirrors Python SDK: agentcp/group/operations.py
4
4
  */
5
5
  import { ACPGroupClient } from './client';
6
- import { CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, CursorState, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp } from './types';
6
+ import { RegisterOnlineResp, CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, CursorState, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp } from './types';
7
7
  /**
8
8
  * Callback interface for syncGroup.
9
9
  */
@@ -42,15 +42,36 @@ export declare class GroupOperations {
42
42
  inviteCode?: string;
43
43
  message?: string;
44
44
  }): Promise<string>;
45
+ /**
46
+ * 注册上线,获取游标状态。
47
+ * 客户端每次启动或重新连接时,必须对每个群组调用。
48
+ */
49
+ registerOnline(targetAid: string, groupId: string): Promise<RegisterOnlineResp>;
50
+ /**
51
+ * 主动下线(优雅退出)。
52
+ * 客户端退出时调用,立即从在线列表移除。
53
+ */
54
+ unregisterOnline(targetAid: string, groupId: string): Promise<void>;
55
+ /**
56
+ * 心跳保活。
57
+ * 在线注册有 5 分钟超时,SDK 需定时发送(建议 2~4 分钟)。
58
+ */
59
+ heartbeat(targetAid: string, groupId: string): Promise<void>;
45
60
  createGroup(targetAid: string, name: string, options?: {
46
61
  alias?: string;
47
62
  subject?: string;
48
63
  visibility?: string;
64
+ description?: string;
49
65
  tags?: string[];
50
66
  }): Promise<CreateGroupResp>;
51
67
  addMember(targetAid: string, groupId: string, agentId: string, role?: string): Promise<void>;
52
68
  sendGroupMessage(targetAid: string, groupId: string, content: string, contentType?: string, metadata?: Record<string, any>): Promise<SendMessageResp>;
53
- pullMessages(targetAid: string, groupId: string, afterMsgId: number, limit?: number): Promise<PullMessagesResp>;
69
+ /**
70
+ * 拉取消息。
71
+ * - afterMsgId > 0: 指定位置模式,从该 ID 之后开始拉取
72
+ * - afterMsgId = 0 或不传: 自动游标模式(推荐),服务端基于 current_msg_id 自动计算
73
+ */
74
+ pullMessages(targetAid: string, groupId: string, afterMsgId?: number, limit?: number): Promise<PullMessagesResp>;
54
75
  ackMessages(targetAid: string, groupId: string, msgId: number): Promise<void>;
55
76
  pullEvents(targetAid: string, groupId: string, afterEventId: number, limit?: number): Promise<PullEventsResp>;
56
77
  ackEvents(targetAid: string, groupId: string, eventId: number): Promise<void>;
@@ -59,6 +59,37 @@ class GroupOperations {
59
59
  return this.requestJoin(targetAid, groupId, (_a = options === null || options === void 0 ? void 0 : options.message) !== null && _a !== void 0 ? _a : '');
60
60
  }
61
61
  // ============================================================
62
+ // Phase 0: Lifecycle (register / heartbeat / unregister)
63
+ // ============================================================
64
+ /**
65
+ * 注册上线,获取游标状态。
66
+ * 客户端每次启动或重新连接时,必须对每个群组调用。
67
+ */
68
+ async registerOnline(targetAid, groupId) {
69
+ const resp = await this._client.sendRequest(targetAid, groupId, "register_online", null);
70
+ this._check(resp, "register_online");
71
+ const d = resp.data || {};
72
+ return {
73
+ msg_cursor: (0, types_1.createMsgCursor)(d.msg_cursor),
74
+ };
75
+ }
76
+ /**
77
+ * 主动下线(优雅退出)。
78
+ * 客户端退出时调用,立即从在线列表移除。
79
+ */
80
+ async unregisterOnline(targetAid, groupId) {
81
+ const resp = await this._client.sendRequest(targetAid, groupId, "unregister_online", null);
82
+ this._check(resp, "unregister_online");
83
+ }
84
+ /**
85
+ * 心跳保活。
86
+ * 在线注册有 5 分钟超时,SDK 需定时发送(建议 2~4 分钟)。
87
+ */
88
+ async heartbeat(targetAid, groupId) {
89
+ const resp = await this._client.sendRequest(targetAid, groupId, "heartbeat", null);
90
+ this._check(resp, "heartbeat");
91
+ }
92
+ // ============================================================
62
93
  // Phase 1: Basic Operations
63
94
  // ============================================================
64
95
  async createGroup(targetAid, name, options) {
@@ -70,6 +101,8 @@ class GroupOperations {
70
101
  params.subject = options.subject;
71
102
  if (options === null || options === void 0 ? void 0 : options.visibility)
72
103
  params.visibility = options.visibility;
104
+ if (options === null || options === void 0 ? void 0 : options.description)
105
+ params.description = options.description;
73
106
  if (options === null || options === void 0 ? void 0 : options.tags)
74
107
  params.tags = options.tags;
75
108
  const resp = await this._client.sendRequest(targetAid, "", "create_group", params);
@@ -96,12 +129,19 @@ class GroupOperations {
96
129
  const d = resp.data || {};
97
130
  return { msg_id: (_a = d.msg_id) !== null && _a !== void 0 ? _a : 0, timestamp: (_b = d.timestamp) !== null && _b !== void 0 ? _b : 0 };
98
131
  }
99
- async pullMessages(targetAid, groupId, afterMsgId, limit = 0) {
132
+ /**
133
+ * 拉取消息。
134
+ * - afterMsgId > 0: 指定位置模式,从该 ID 之后开始拉取
135
+ * - afterMsgId = 0 或不传: 自动游标模式(推荐),服务端基于 current_msg_id 自动计算
136
+ */
137
+ async pullMessages(targetAid, groupId, afterMsgId = 0, limit = 0) {
100
138
  var _a, _b, _c;
101
- const params = { after_msg_id: afterMsgId };
139
+ const params = {};
140
+ if (afterMsgId > 0)
141
+ params.after_msg_id = afterMsgId;
102
142
  if (limit > 0)
103
143
  params.limit = limit;
104
- const resp = await this._client.sendRequest(targetAid, groupId, "pull_messages", params);
144
+ const resp = await this._client.sendRequest(targetAid, groupId, "pull_messages", Object.keys(params).length > 0 ? params : null);
105
145
  this._check(resp, "pull_messages");
106
146
  const d = resp.data || {};
107
147
  return {
@@ -85,6 +85,9 @@ export interface CursorState {
85
85
  msg_cursor: MsgCursor;
86
86
  event_cursor: EventCursor;
87
87
  }
88
+ export interface RegisterOnlineResp {
89
+ msg_cursor: MsgCursor;
90
+ }
88
91
  export interface CreateGroupResp {
89
92
  group_id: string;
90
93
  group_url: string;