acp-ts 1.2.3 → 1.2.4
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.js +40 -36
- package/dist/agentws.d.ts +8 -2
- package/dist/agentws.js +22 -4
- package/dist/api.js +8 -8
- package/dist/cert.js +3 -2
- package/dist/cli.js +16 -15
- package/dist/datamanager.js +27 -11
- package/dist/filesync.js +11 -10
- package/dist/group/client.js +44 -36
- package/dist/group/cursor_store.d.ts +4 -9
- package/dist/group/cursor_store.js +12 -40
- package/dist/group/events.d.ts +2 -2
- package/dist/group/events.js +7 -27
- package/dist/group/index.d.ts +1 -1
- package/dist/group/index.js +2 -2
- package/dist/group/message_store.d.ts +5 -6
- package/dist/group/message_store.js +64 -72
- package/dist/group/operations.d.ts +1 -5
- package/dist/group/operations.js +5 -12
- package/dist/group/types.d.ts +3 -29
- package/dist/group/types.js +2 -6
- package/dist/heartbeat.js +24 -24
- package/dist/messagestore.js +9 -8
- package/dist/server.js +2053 -1699
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +19 -2
- package/dist/websocket.d.ts +12 -1
- package/dist/websocket.js +61 -28
- package/package.json +1 -1
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.GroupMessageStore = void 0;
|
|
13
13
|
const websocket_1 = require("../websocket");
|
|
14
|
+
const utils_1 = require("../utils");
|
|
14
15
|
class GroupMessageStore {
|
|
15
16
|
constructor(options) {
|
|
16
17
|
var _a, _b;
|
|
17
18
|
this.ownerAid = '';
|
|
18
19
|
this.groups = new Map();
|
|
19
20
|
this._indexDirty = false;
|
|
20
|
-
this._flushIndexPromise = null;
|
|
21
21
|
this.persistMessages = options.persistMessages;
|
|
22
22
|
this.basePath = options.basePath;
|
|
23
23
|
this.maxMessagesPerGroup = (_a = options.maxMessagesPerGroup) !== null && _a !== void 0 ? _a : 5000;
|
|
@@ -44,12 +44,12 @@ class GroupMessageStore {
|
|
|
44
44
|
const path = require('path');
|
|
45
45
|
return path.join(this.getGroupDir(aid, groupId), 'events.jsonl');
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
ensureDir(dir) {
|
|
48
48
|
if (!websocket_1.isNodeEnvironment)
|
|
49
49
|
return;
|
|
50
50
|
const fs = require('fs');
|
|
51
51
|
if (!fs.existsSync(dir)) {
|
|
52
|
-
|
|
52
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
// ---- load ----
|
|
@@ -64,45 +64,41 @@ class GroupMessageStore {
|
|
|
64
64
|
if (!fs.existsSync(indexPath))
|
|
65
65
|
return;
|
|
66
66
|
try {
|
|
67
|
-
const raw =
|
|
67
|
+
const raw = fs.readFileSync(indexPath, 'utf-8');
|
|
68
68
|
const records = JSON.parse(raw);
|
|
69
69
|
if (!Array.isArray(records))
|
|
70
70
|
return;
|
|
71
71
|
for (const r of records) {
|
|
72
|
-
const messages =
|
|
73
|
-
const events =
|
|
72
|
+
const messages = this.readJsonl(this.getMessagesPath(ownerAid, r.groupId));
|
|
73
|
+
const events = this.readJsonl(this.getEventsPath(ownerAid, r.groupId));
|
|
74
74
|
this.groups.set(r.groupId, { record: r, messages, events });
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
catch (e) {
|
|
78
|
-
|
|
78
|
+
utils_1.logger.error('[GroupMessageStore] 加载索引失败:', e);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
readJsonl(filePath) {
|
|
82
82
|
if (!websocket_1.isNodeEnvironment)
|
|
83
83
|
return [];
|
|
84
84
|
const fs = require('fs');
|
|
85
85
|
if (!fs.existsSync(filePath))
|
|
86
86
|
return [];
|
|
87
87
|
try {
|
|
88
|
-
const raw =
|
|
88
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
89
89
|
const items = [];
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const l = lines[i].trim();
|
|
90
|
+
for (const line of raw.split('\n')) {
|
|
91
|
+
const l = line.trim();
|
|
93
92
|
if (!l)
|
|
94
93
|
continue;
|
|
95
94
|
try {
|
|
96
95
|
items.push(JSON.parse(l));
|
|
97
96
|
}
|
|
98
|
-
catch (
|
|
99
|
-
console.warn(`[GroupMessageStore] JSONL 解析失败 (${filePath} 行${i + 1}): ${l.substring(0, 80)}`, lineErr);
|
|
100
|
-
}
|
|
97
|
+
catch (_a) { }
|
|
101
98
|
}
|
|
102
99
|
return items;
|
|
103
100
|
}
|
|
104
|
-
catch (
|
|
105
|
-
console.warn(`[GroupMessageStore] 读取 JSONL 文件失败 (${filePath}):`, e);
|
|
101
|
+
catch (_b) {
|
|
106
102
|
return [];
|
|
107
103
|
}
|
|
108
104
|
}
|
|
@@ -119,7 +115,7 @@ class GroupMessageStore {
|
|
|
119
115
|
data = { record, messages: [], events: [] };
|
|
120
116
|
this.groups.set(groupId, data);
|
|
121
117
|
this._indexDirty = true;
|
|
122
|
-
this.
|
|
118
|
+
this.flushIndexAsync();
|
|
123
119
|
}
|
|
124
120
|
return data.record;
|
|
125
121
|
}
|
|
@@ -147,17 +143,18 @@ class GroupMessageStore {
|
|
|
147
143
|
}
|
|
148
144
|
}
|
|
149
145
|
catch (e) {
|
|
150
|
-
|
|
146
|
+
utils_1.logger.error(`[GroupMessageStore] 删除群目录失败 (${groupId}):`, e);
|
|
151
147
|
}
|
|
152
148
|
await this.flushIndex();
|
|
153
149
|
}
|
|
154
150
|
return true;
|
|
155
151
|
}
|
|
156
152
|
// ---- message storage ----
|
|
157
|
-
|
|
153
|
+
addMessage(groupId, msg) {
|
|
158
154
|
const data = this.groups.get(groupId);
|
|
159
155
|
if (!data)
|
|
160
156
|
return;
|
|
157
|
+
// dedup: skip if msg_id already seen
|
|
161
158
|
if (msg.msg_id <= data.record.lastMsgId)
|
|
162
159
|
return;
|
|
163
160
|
data.messages.push(msg);
|
|
@@ -165,35 +162,37 @@ class GroupMessageStore {
|
|
|
165
162
|
data.record.messageCount = data.messages.length;
|
|
166
163
|
data.record.lastMessageAt = msg.timestamp || Date.now();
|
|
167
164
|
this._indexDirty = true;
|
|
165
|
+
// truncate if over limit
|
|
168
166
|
if (data.messages.length > this.maxMessagesPerGroup) {
|
|
169
167
|
const excess = data.messages.length - this.maxMessagesPerGroup;
|
|
170
168
|
data.messages.splice(0, excess);
|
|
171
169
|
data.record.messageCount = data.messages.length;
|
|
172
|
-
|
|
170
|
+
this.flushMessages(groupId);
|
|
173
171
|
}
|
|
174
172
|
else {
|
|
175
|
-
|
|
173
|
+
this.appendJsonl(this.getMessagesPath(this.ownerAid, groupId), msg);
|
|
176
174
|
}
|
|
177
|
-
this.
|
|
175
|
+
this.flushIndexAsync();
|
|
178
176
|
}
|
|
179
|
-
|
|
177
|
+
addMessages(groupId, msgs) {
|
|
180
178
|
const data = this.groups.get(groupId);
|
|
181
|
-
if (!data)
|
|
179
|
+
if (!data) {
|
|
180
|
+
utils_1.logger.warn(`[GroupMessageStore] addMessages: group=${groupId} NOT FOUND in store! Available groups: [${Array.from(this.groups.keys()).join(', ')}]`);
|
|
182
181
|
return;
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
}
|
|
183
|
+
utils_1.logger.log(`[GroupMessageStore] addMessages: group=${groupId} incoming=${msgs.length} currentLastMsgId=${data.record.lastMsgId} incomingMsgIds=[${msgs.map(m => m.msg_id).join(',')}]`);
|
|
185
184
|
let added = 0;
|
|
186
|
-
for (const msg of
|
|
187
|
-
if (
|
|
185
|
+
for (const msg of msgs) {
|
|
186
|
+
if (msg.msg_id <= data.record.lastMsgId) {
|
|
187
|
+
utils_1.logger.log(`[GroupMessageStore] addMessages: SKIP duplicate msg_id=${msg.msg_id} <= lastMsgId=${data.record.lastMsgId}`);
|
|
188
188
|
continue;
|
|
189
|
-
data.messages.push(msg);
|
|
190
|
-
existingIds.add(msg.msg_id);
|
|
191
|
-
if (msg.msg_id > data.record.lastMsgId) {
|
|
192
|
-
data.record.lastMsgId = msg.msg_id;
|
|
193
189
|
}
|
|
190
|
+
data.messages.push(msg);
|
|
191
|
+
data.record.lastMsgId = msg.msg_id;
|
|
194
192
|
data.record.lastMessageAt = msg.timestamp || Date.now();
|
|
195
193
|
added++;
|
|
196
194
|
}
|
|
195
|
+
utils_1.logger.log(`[GroupMessageStore] addMessages result: group=${groupId} added=${added}/${msgs.length} newLastMsgId=${data.record.lastMsgId} totalMessages=${data.messages.length}`);
|
|
197
196
|
if (added === 0)
|
|
198
197
|
return;
|
|
199
198
|
data.record.messageCount = data.messages.length;
|
|
@@ -202,9 +201,12 @@ class GroupMessageStore {
|
|
|
202
201
|
const excess = data.messages.length - this.maxMessagesPerGroup;
|
|
203
202
|
data.messages.splice(0, excess);
|
|
204
203
|
data.record.messageCount = data.messages.length;
|
|
204
|
+
this.flushMessages(groupId);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.flushMessages(groupId);
|
|
205
208
|
}
|
|
206
|
-
|
|
207
|
-
this.flushIndexQueued();
|
|
209
|
+
this.flushIndexAsync();
|
|
208
210
|
}
|
|
209
211
|
getMessages(groupId, options) {
|
|
210
212
|
const data = this.groups.get(groupId);
|
|
@@ -229,7 +231,7 @@ class GroupMessageStore {
|
|
|
229
231
|
return data.messages.slice(-limit);
|
|
230
232
|
}
|
|
231
233
|
// ---- event storage ----
|
|
232
|
-
|
|
234
|
+
addEvent(groupId, evt) {
|
|
233
235
|
const data = this.groups.get(groupId);
|
|
234
236
|
if (!data)
|
|
235
237
|
return;
|
|
@@ -243,14 +245,14 @@ class GroupMessageStore {
|
|
|
243
245
|
const excess = data.events.length - this.maxEventsPerGroup;
|
|
244
246
|
data.events.splice(0, excess);
|
|
245
247
|
data.record.eventCount = data.events.length;
|
|
246
|
-
|
|
248
|
+
this.flushEvents(groupId);
|
|
247
249
|
}
|
|
248
250
|
else {
|
|
249
|
-
|
|
251
|
+
this.appendJsonl(this.getEventsPath(this.ownerAid, groupId), evt);
|
|
250
252
|
}
|
|
251
|
-
this.
|
|
253
|
+
this.flushIndexAsync();
|
|
252
254
|
}
|
|
253
|
-
|
|
255
|
+
addEvents(groupId, evts) {
|
|
254
256
|
const data = this.groups.get(groupId);
|
|
255
257
|
if (!data)
|
|
256
258
|
return;
|
|
@@ -271,8 +273,8 @@ class GroupMessageStore {
|
|
|
271
273
|
data.events.splice(0, excess);
|
|
272
274
|
data.record.eventCount = data.events.length;
|
|
273
275
|
}
|
|
274
|
-
|
|
275
|
-
this.
|
|
276
|
+
this.flushEvents(groupId);
|
|
277
|
+
this.flushIndexAsync();
|
|
276
278
|
}
|
|
277
279
|
getEvents(groupId, options) {
|
|
278
280
|
const data = this.groups.get(groupId);
|
|
@@ -288,54 +290,47 @@ class GroupMessageStore {
|
|
|
288
290
|
return result;
|
|
289
291
|
}
|
|
290
292
|
// ---- persistence ----
|
|
291
|
-
|
|
293
|
+
appendJsonl(filePath, item) {
|
|
292
294
|
if (!this.persistMessages || !websocket_1.isNodeEnvironment || !this.ownerAid)
|
|
293
295
|
return;
|
|
294
296
|
const fs = require('fs');
|
|
295
297
|
const path = require('path');
|
|
296
|
-
|
|
298
|
+
this.ensureDir(path.dirname(filePath));
|
|
297
299
|
try {
|
|
298
|
-
|
|
300
|
+
fs.appendFileSync(filePath, JSON.stringify(item) + '\n');
|
|
299
301
|
}
|
|
300
302
|
catch (e) {
|
|
301
|
-
|
|
303
|
+
utils_1.logger.error(`[GroupMessageStore] 追加写入失败 (${filePath}):`, e);
|
|
302
304
|
}
|
|
303
305
|
}
|
|
304
|
-
|
|
306
|
+
writeJsonl(filePath, items) {
|
|
305
307
|
if (!this.persistMessages || !websocket_1.isNodeEnvironment || !this.ownerAid)
|
|
306
308
|
return;
|
|
307
309
|
const fs = require('fs');
|
|
308
310
|
const path = require('path');
|
|
309
|
-
|
|
311
|
+
this.ensureDir(path.dirname(filePath));
|
|
310
312
|
try {
|
|
311
313
|
const lines = items.map(i => JSON.stringify(i)).join('\n');
|
|
312
|
-
|
|
313
|
-
const tmpPath = filePath + '.tmp';
|
|
314
|
-
await fs.promises.writeFile(tmpPath, content);
|
|
315
|
-
await fs.promises.rename(tmpPath, filePath);
|
|
314
|
+
fs.writeFileSync(filePath, lines ? lines + '\n' : '');
|
|
316
315
|
}
|
|
317
316
|
catch (e) {
|
|
318
|
-
|
|
317
|
+
utils_1.logger.error(`[GroupMessageStore] 全量写入失败 (${filePath}):`, e);
|
|
319
318
|
}
|
|
320
319
|
}
|
|
321
|
-
|
|
320
|
+
flushMessages(groupId) {
|
|
322
321
|
const data = this.groups.get(groupId);
|
|
323
322
|
if (!data || !this.ownerAid)
|
|
324
323
|
return;
|
|
325
|
-
|
|
324
|
+
this.writeJsonl(this.getMessagesPath(this.ownerAid, groupId), data.messages);
|
|
326
325
|
}
|
|
327
|
-
|
|
326
|
+
flushEvents(groupId) {
|
|
328
327
|
const data = this.groups.get(groupId);
|
|
329
328
|
if (!data || !this.ownerAid)
|
|
330
329
|
return;
|
|
331
|
-
|
|
330
|
+
this.writeJsonl(this.getEventsPath(this.ownerAid, groupId), data.events);
|
|
332
331
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const prev = (_a = this._flushIndexPromise) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
336
|
-
this._flushIndexPromise = prev.then(() => this.flushIndex()).catch(e => {
|
|
337
|
-
console.error('[GroupMessageStore] 索引刷盘失败:', e);
|
|
338
|
-
});
|
|
332
|
+
flushIndexAsync() {
|
|
333
|
+
this.flushIndex().catch(() => { });
|
|
339
334
|
}
|
|
340
335
|
async flushIndex() {
|
|
341
336
|
if (!this.persistMessages || !websocket_1.isNodeEnvironment || !this.ownerAid)
|
|
@@ -344,18 +339,15 @@ class GroupMessageStore {
|
|
|
344
339
|
return;
|
|
345
340
|
const fs = require('fs');
|
|
346
341
|
const dir = this.getGroupsDir(this.ownerAid);
|
|
347
|
-
|
|
342
|
+
this.ensureDir(dir);
|
|
348
343
|
const records = Array.from(this.groups.values())
|
|
349
344
|
.map(d => d.record);
|
|
350
345
|
try {
|
|
351
|
-
|
|
352
|
-
const tmpPath = indexPath + '.tmp';
|
|
353
|
-
await fs.promises.writeFile(tmpPath, JSON.stringify(records, null, 2));
|
|
354
|
-
await fs.promises.rename(tmpPath, indexPath);
|
|
346
|
+
fs.writeFileSync(this.getIndexPath(this.ownerAid), JSON.stringify(records, null, 2));
|
|
355
347
|
this._indexDirty = false;
|
|
356
348
|
}
|
|
357
349
|
catch (e) {
|
|
358
|
-
|
|
350
|
+
utils_1.logger.error('[GroupMessageStore] 写入索引失败:', e);
|
|
359
351
|
}
|
|
360
352
|
}
|
|
361
353
|
async flush(ownerAid) {
|
|
@@ -363,9 +355,9 @@ class GroupMessageStore {
|
|
|
363
355
|
return;
|
|
364
356
|
this.ownerAid = ownerAid;
|
|
365
357
|
await this.flushIndex();
|
|
366
|
-
for (const [groupId] of this.groups) {
|
|
367
|
-
|
|
368
|
-
|
|
358
|
+
for (const [groupId, data] of this.groups) {
|
|
359
|
+
this.flushMessages(groupId);
|
|
360
|
+
this.flushEvents(groupId);
|
|
369
361
|
}
|
|
370
362
|
}
|
|
371
363
|
async flushAll() {
|
|
@@ -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, RequestJoinResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, DutyConfig, DutyStatusResp
|
|
6
|
+
import { CreateGroupResp, SendMessageResp, PullMessagesResp, PullEventsResp, CursorState, GroupInfoResp, BanlistResp, BatchReviewResp, PendingRequestsResp, RequestJoinResp, MembersResp, AdminsResp, RulesResp, AnnouncementResp, JoinRequirementsResp, MasterResp, InviteCodeResp, InviteCodeListResp, BroadcastLockResp, BroadcastPermissionResp, SyncStatusResp, SyncLogResp, ChecksumResp, PublicGroupInfoResp, SearchGroupsResp, DigestResp, ListMyGroupsResp, GetFileResp, GetSummaryResp, GetMetricsResp, DutyConfig, DutyStatusResp } from './types';
|
|
7
7
|
/**
|
|
8
8
|
* Callback interface for syncGroup.
|
|
9
9
|
*/
|
|
@@ -146,10 +146,6 @@ export declare class GroupOperations {
|
|
|
146
146
|
* 获取值班状态,包含 config 和 state。
|
|
147
147
|
*/
|
|
148
148
|
getDutyStatus(targetAid: string, groupId: string): Promise<DutyStatusResp>;
|
|
149
|
-
/**
|
|
150
|
-
* 值班 Agent 提交仲裁决策。
|
|
151
|
-
*/
|
|
152
|
-
dispatchDecision(targetAid: string, groupId: string, params: DispatchDecisionParams): Promise<void>;
|
|
153
149
|
/**
|
|
154
150
|
* 重新获取所有成员的 agent.md 并更新 AgentType。
|
|
155
151
|
*/
|
package/dist/group/operations.js
CHANGED
|
@@ -634,11 +634,11 @@ class GroupOperations {
|
|
|
634
634
|
params.shift_duration_ms = config.shift_duration_ms;
|
|
635
635
|
if (config.max_messages_per_shift != null)
|
|
636
636
|
params.max_messages_per_shift = config.max_messages_per_shift;
|
|
637
|
-
if (config.
|
|
638
|
-
params.
|
|
639
|
-
if (config.
|
|
640
|
-
params.
|
|
641
|
-
const resp = await this._client.sendRequest(targetAid, groupId, "update_duty_config", params);
|
|
637
|
+
if (config.duty_priority_window_ms != null)
|
|
638
|
+
params.duty_priority_window_ms = config.duty_priority_window_ms;
|
|
639
|
+
if (config.enable_rule_prelude != null)
|
|
640
|
+
params.enable_rule_prelude = config.enable_rule_prelude;
|
|
641
|
+
const resp = await this._client.sendRequest(targetAid, groupId, "update_duty_config", { duty_config: params });
|
|
642
642
|
this._check(resp, "update_duty_config");
|
|
643
643
|
}
|
|
644
644
|
/**
|
|
@@ -658,13 +658,6 @@ class GroupOperations {
|
|
|
658
658
|
const d = resp.data || {};
|
|
659
659
|
return { config: (_a = d.config) !== null && _a !== void 0 ? _a : {}, state: (_b = d.state) !== null && _b !== void 0 ? _b : {} };
|
|
660
660
|
}
|
|
661
|
-
/**
|
|
662
|
-
* 值班 Agent 提交仲裁决策。
|
|
663
|
-
*/
|
|
664
|
-
async dispatchDecision(targetAid, groupId, params) {
|
|
665
|
-
const resp = await this._client.sendRequest(targetAid, groupId, "dispatch_decision", params);
|
|
666
|
-
this._check(resp, "dispatch_decision");
|
|
667
|
-
}
|
|
668
661
|
/**
|
|
669
662
|
* 重新获取所有成员的 agent.md 并更新 AgentType。
|
|
670
663
|
*/
|
package/dist/group/types.d.ts
CHANGED
|
@@ -20,8 +20,6 @@ export declare enum GroupErrorCode {
|
|
|
20
20
|
ACTION_NOT_IMPL = 1099,
|
|
21
21
|
DUTY_NOT_ENABLED = 1020,
|
|
22
22
|
NOT_DUTY_AGENT = 1021,
|
|
23
|
-
DISPATCH_NOT_FOUND = 1022,
|
|
24
|
-
INVALID_DECISION = 1023,
|
|
25
23
|
AGENT_MD_NOT_FOUND = 1024,
|
|
26
24
|
AGENT_MD_INVALID = 1025
|
|
27
25
|
}
|
|
@@ -258,8 +256,8 @@ export declare const NOTIFY_GROUP_INVITE = "group_invite";
|
|
|
258
256
|
export declare const NOTIFY_JOIN_APPROVED = "join_approved";
|
|
259
257
|
export declare const NOTIFY_JOIN_REJECTED = "join_rejected";
|
|
260
258
|
export declare const NOTIFY_JOIN_REQUEST_RECEIVED = "join_request_received";
|
|
259
|
+
export declare const NOTIFY_GROUP_MESSAGE = "group_message";
|
|
261
260
|
export declare const NOTIFY_GROUP_EVENT = "group_event";
|
|
262
|
-
export declare const NOTIFY_DUTY_DISPATCH = "duty_dispatch";
|
|
263
261
|
export declare const EVENT_MEMBER_JOINED = "member_joined";
|
|
264
262
|
export declare const EVENT_MEMBER_REMOVED = "member_removed";
|
|
265
263
|
export declare const EVENT_MEMBER_LEFT = "member_left";
|
|
@@ -275,37 +273,13 @@ export declare const EVENT_MEMBER_UNBANNED = "member_unbanned";
|
|
|
275
273
|
export declare const EVENT_JOIN_REQUIREMENTS_UPDATED = "join_requirements_updated";
|
|
276
274
|
export declare const EVENT_INVITE_CODE_CREATED = "invite_code_created";
|
|
277
275
|
export declare const EVENT_INVITE_CODE_REVOKED = "invite_code_revoked";
|
|
278
|
-
export interface DutyMemberInfo {
|
|
279
|
-
agent_id: string;
|
|
280
|
-
agent_type: string;
|
|
281
|
-
}
|
|
282
|
-
export interface DutyContext {
|
|
283
|
-
needs_dispatch: boolean;
|
|
284
|
-
original_msg_id: number;
|
|
285
|
-
sender_id: string;
|
|
286
|
-
sender_type: string;
|
|
287
|
-
group_member_count: number;
|
|
288
|
-
online_ai_members: DutyMemberInfo[];
|
|
289
|
-
human_members: DutyMemberInfo[];
|
|
290
|
-
}
|
|
291
|
-
export interface DispatchDecisionParams {
|
|
292
|
-
original_msg_id: number;
|
|
293
|
-
type: "broadcast" | "selective" | "suppress";
|
|
294
|
-
hint?: string;
|
|
295
|
-
reply_mode?: string;
|
|
296
|
-
}
|
|
297
|
-
export interface DispatchMetadata {
|
|
298
|
-
type: string;
|
|
299
|
-
hint: string;
|
|
300
|
-
reply_mode: string;
|
|
301
|
-
}
|
|
302
276
|
export interface DutyConfig {
|
|
303
277
|
mode: "none" | "fixed" | "rotation";
|
|
304
278
|
rotation_strategy?: "round_robin" | "random";
|
|
305
279
|
shift_duration_ms?: number;
|
|
306
280
|
max_messages_per_shift?: number;
|
|
307
|
-
|
|
308
|
-
|
|
281
|
+
duty_priority_window_ms?: number;
|
|
282
|
+
enable_rule_prelude?: boolean;
|
|
309
283
|
agents?: string[];
|
|
310
284
|
}
|
|
311
285
|
export interface DutyState {
|
package/dist/group/types.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Mirrors Python SDK: agentcp/group/types.py
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ACTION_MESSAGE_BATCH_PUSH = exports.EVENT_INVITE_CODE_REVOKED = exports.EVENT_INVITE_CODE_CREATED = exports.EVENT_JOIN_REQUIREMENTS_UPDATED = exports.EVENT_MEMBER_UNBANNED = exports.EVENT_GROUP_RESUMED = exports.EVENT_GROUP_SUSPENDED = exports.EVENT_MASTER_TRANSFERRED = exports.EVENT_GROUP_DISSOLVED = exports.EVENT_ANNOUNCEMENT_UPDATED = exports.EVENT_RULES_UPDATED = exports.EVENT_META_UPDATED = exports.EVENT_MEMBER_BANNED = exports.EVENT_MEMBER_LEFT = exports.EVENT_MEMBER_REMOVED = exports.EVENT_MEMBER_JOINED = exports.
|
|
7
|
+
exports.ACTION_MESSAGE_BATCH_PUSH = exports.EVENT_INVITE_CODE_REVOKED = exports.EVENT_INVITE_CODE_CREATED = exports.EVENT_JOIN_REQUIREMENTS_UPDATED = exports.EVENT_MEMBER_UNBANNED = exports.EVENT_GROUP_RESUMED = exports.EVENT_GROUP_SUSPENDED = exports.EVENT_MASTER_TRANSFERRED = exports.EVENT_GROUP_DISSOLVED = exports.EVENT_ANNOUNCEMENT_UPDATED = exports.EVENT_RULES_UPDATED = exports.EVENT_META_UPDATED = exports.EVENT_MEMBER_BANNED = exports.EVENT_MEMBER_LEFT = exports.EVENT_MEMBER_REMOVED = exports.EVENT_MEMBER_JOINED = exports.NOTIFY_GROUP_EVENT = exports.NOTIFY_GROUP_MESSAGE = exports.NOTIFY_JOIN_REQUEST_RECEIVED = exports.NOTIFY_JOIN_REJECTED = exports.NOTIFY_JOIN_APPROVED = exports.NOTIFY_GROUP_INVITE = exports.NOTIFY_NEW_EVENT = exports.NOTIFY_NEW_MESSAGE = exports.GroupError = exports.GroupErrorCode = void 0;
|
|
8
8
|
exports.buildGroupRequest = buildGroupRequest;
|
|
9
9
|
exports.groupRequestToJson = groupRequestToJson;
|
|
10
10
|
exports.parseGroupResponse = parseGroupResponse;
|
|
@@ -34,8 +34,6 @@ var GroupErrorCode;
|
|
|
34
34
|
// Duty error codes
|
|
35
35
|
GroupErrorCode[GroupErrorCode["DUTY_NOT_ENABLED"] = 1020] = "DUTY_NOT_ENABLED";
|
|
36
36
|
GroupErrorCode[GroupErrorCode["NOT_DUTY_AGENT"] = 1021] = "NOT_DUTY_AGENT";
|
|
37
|
-
GroupErrorCode[GroupErrorCode["DISPATCH_NOT_FOUND"] = 1022] = "DISPATCH_NOT_FOUND";
|
|
38
|
-
GroupErrorCode[GroupErrorCode["INVALID_DECISION"] = 1023] = "INVALID_DECISION";
|
|
39
37
|
GroupErrorCode[GroupErrorCode["AGENT_MD_NOT_FOUND"] = 1024] = "AGENT_MD_NOT_FOUND";
|
|
40
38
|
GroupErrorCode[GroupErrorCode["AGENT_MD_INVALID"] = 1025] = "AGENT_MD_INVALID";
|
|
41
39
|
})(GroupErrorCode || (exports.GroupErrorCode = GroupErrorCode = {}));
|
|
@@ -56,8 +54,6 @@ const CODE_MESSAGES = {
|
|
|
56
54
|
1013: "broadcast conflict",
|
|
57
55
|
1020: "duty not enabled",
|
|
58
56
|
1021: "not duty agent",
|
|
59
|
-
1022: "dispatch not found",
|
|
60
|
-
1023: "invalid decision",
|
|
61
57
|
1024: "agent.md not found",
|
|
62
58
|
1025: "agent.md invalid",
|
|
63
59
|
1099: "action not implemented",
|
|
@@ -133,8 +129,8 @@ exports.NOTIFY_GROUP_INVITE = "group_invite";
|
|
|
133
129
|
exports.NOTIFY_JOIN_APPROVED = "join_approved";
|
|
134
130
|
exports.NOTIFY_JOIN_REJECTED = "join_rejected";
|
|
135
131
|
exports.NOTIFY_JOIN_REQUEST_RECEIVED = "join_request_received";
|
|
132
|
+
exports.NOTIFY_GROUP_MESSAGE = "group_message";
|
|
136
133
|
exports.NOTIFY_GROUP_EVENT = "group_event";
|
|
137
|
-
exports.NOTIFY_DUTY_DISPATCH = "duty_dispatch";
|
|
138
134
|
// Group Event Type Constants
|
|
139
135
|
exports.EVENT_MEMBER_JOINED = "member_joined";
|
|
140
136
|
exports.EVENT_MEMBER_REMOVED = "member_removed";
|
package/dist/heartbeat.js
CHANGED
|
@@ -106,12 +106,12 @@ class HeartbeatClient {
|
|
|
106
106
|
try {
|
|
107
107
|
const privateKey = await (0, utils_1.getDecryptKey)(this.agentId, this.seedPassword);
|
|
108
108
|
if (!privateKey) {
|
|
109
|
-
|
|
109
|
+
utils_1.logger.error('[Heartbeat] 获取私钥失败');
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
112
112
|
const { publicKeyPem, certPem } = await (0, cert_1.getPublicKeyPem)(this.agentId);
|
|
113
113
|
if (!certPem) {
|
|
114
|
-
|
|
114
|
+
utils_1.logger.error('[Heartbeat] 获取证书失败');
|
|
115
115
|
return false;
|
|
116
116
|
}
|
|
117
117
|
// 确保使用 HTTPS
|
|
@@ -121,23 +121,23 @@ class HeartbeatClient {
|
|
|
121
121
|
}
|
|
122
122
|
const result = await (0, api_1.signIn)(this.agentId, serverUrl, privateKey, publicKeyPem, certPem);
|
|
123
123
|
if (!result || !result.signData) {
|
|
124
|
-
|
|
124
|
+
utils_1.logger.error('[Heartbeat] signIn 失败');
|
|
125
125
|
return false;
|
|
126
126
|
}
|
|
127
127
|
const { server_ip, port, sign_cookie, signature } = result.signData;
|
|
128
128
|
if (!server_ip || !port || sign_cookie === undefined) {
|
|
129
|
-
|
|
129
|
+
utils_1.logger.error('[Heartbeat] signIn 返回数据不完整');
|
|
130
130
|
return false;
|
|
131
131
|
}
|
|
132
132
|
this.serverIp = server_ip;
|
|
133
133
|
this.port = parseInt(port, 10);
|
|
134
134
|
this.signCookie = parseInt(sign_cookie, 10);
|
|
135
135
|
this.signature = signature || '';
|
|
136
|
-
|
|
136
|
+
utils_1.logger.log(`[Heartbeat] signIn 成功: ${this.serverIp}:${this.port}`);
|
|
137
137
|
return true;
|
|
138
138
|
}
|
|
139
139
|
catch (error) {
|
|
140
|
-
|
|
140
|
+
utils_1.logger.error('[Heartbeat] signIn 异常:', error);
|
|
141
141
|
return false;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
@@ -151,7 +151,7 @@ class HeartbeatClient {
|
|
|
151
151
|
this.handleMessage(msg);
|
|
152
152
|
});
|
|
153
153
|
this.udpSocket.on('error', (err) => {
|
|
154
|
-
|
|
154
|
+
utils_1.logger.error('[Heartbeat] Socket 错误:', err);
|
|
155
155
|
this.recvFailures++;
|
|
156
156
|
if (this.recvFailures >= HeartbeatClient.MAX_RECV_FAILURES) {
|
|
157
157
|
this.reconnect('recv_failures_threshold');
|
|
@@ -192,7 +192,7 @@ class HeartbeatClient {
|
|
|
192
192
|
this.recvFailures = 0;
|
|
193
193
|
// 检查是否需要重新认证
|
|
194
194
|
if (resp.NextBeat === 401) {
|
|
195
|
-
|
|
195
|
+
utils_1.logger.warn('[Heartbeat] 收到 401,需要重新认证');
|
|
196
196
|
this.reconnect('401_auth_failed');
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
@@ -204,7 +204,7 @@ class HeartbeatClient {
|
|
|
204
204
|
else if (header.MessageType === message_serialize_1.MessageType.INVITE_REQ) {
|
|
205
205
|
// 邀请请求
|
|
206
206
|
const { req } = message_serialize_1.InviteMessageReq.deserialize(data, 0);
|
|
207
|
-
|
|
207
|
+
utils_1.logger.log(`[Heartbeat] 收到邀请请求: inviter=${req.InviterAgentId}, session=${req.SessionId}`);
|
|
208
208
|
// 发送邀请响应
|
|
209
209
|
this.sendInviteResponse(req);
|
|
210
210
|
// 触发回调
|
|
@@ -219,7 +219,7 @@ class HeartbeatClient {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
catch (error) {
|
|
222
|
-
|
|
222
|
+
utils_1.logger.error('[Heartbeat] 处理消息异常:', error);
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
@@ -242,15 +242,15 @@ class HeartbeatClient {
|
|
|
242
242
|
const data = resp.serialize();
|
|
243
243
|
this.udpSocket.send(data, this.port, this.serverIp, (err) => {
|
|
244
244
|
if (err) {
|
|
245
|
-
|
|
245
|
+
utils_1.logger.error('[Heartbeat] 发送邀请响应失败:', err);
|
|
246
246
|
}
|
|
247
247
|
else {
|
|
248
|
-
|
|
248
|
+
utils_1.logger.log('[Heartbeat] 邀请响应已发送');
|
|
249
249
|
}
|
|
250
250
|
});
|
|
251
251
|
}
|
|
252
252
|
catch (error) {
|
|
253
|
-
|
|
253
|
+
utils_1.logger.error('[Heartbeat] 发送邀请响应异常:', error);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
/**
|
|
@@ -265,7 +265,7 @@ class HeartbeatClient {
|
|
|
265
265
|
if (this.lastHbRecv > 0) {
|
|
266
266
|
const timeoutThreshold = HeartbeatClient.MAX_MISSED_HEARTBEATS * this.heartbeatInterval;
|
|
267
267
|
if (now - this.lastHbRecv > timeoutThreshold) {
|
|
268
|
-
|
|
268
|
+
utils_1.logger.warn(`[Heartbeat] 心跳响应超时: ${now - this.lastHbRecv}ms > ${timeoutThreshold}ms`);
|
|
269
269
|
this.reconnect('heartbeat_response_timeout');
|
|
270
270
|
return;
|
|
271
271
|
}
|
|
@@ -287,7 +287,7 @@ class HeartbeatClient {
|
|
|
287
287
|
const data = req.serialize();
|
|
288
288
|
this.udpSocket.send(data, this.port, this.serverIp, (err) => {
|
|
289
289
|
if (err) {
|
|
290
|
-
|
|
290
|
+
utils_1.logger.error('[Heartbeat] 发送心跳失败:', err);
|
|
291
291
|
this.sendFailures++;
|
|
292
292
|
if (this.sendFailures >= HeartbeatClient.MAX_SEND_FAILURES) {
|
|
293
293
|
this.reconnect('send_failures_threshold');
|
|
@@ -299,7 +299,7 @@ class HeartbeatClient {
|
|
|
299
299
|
});
|
|
300
300
|
}
|
|
301
301
|
catch (error) {
|
|
302
|
-
|
|
302
|
+
utils_1.logger.error('[Heartbeat] 发送心跳异常:', error);
|
|
303
303
|
this.sendFailures++;
|
|
304
304
|
}
|
|
305
305
|
}
|
|
@@ -318,14 +318,14 @@ class HeartbeatClient {
|
|
|
318
318
|
// 限流:至少间隔 5 秒
|
|
319
319
|
if (elapsed < 5000) {
|
|
320
320
|
const backoff = Math.min(5000 - elapsed, HeartbeatClient.RECONNECT_BACKOFF_MAX);
|
|
321
|
-
|
|
321
|
+
utils_1.logger.log(`[Heartbeat] 重连退避: ${backoff}ms`);
|
|
322
322
|
await new Promise(resolve => setTimeout(resolve, backoff));
|
|
323
323
|
}
|
|
324
|
-
|
|
324
|
+
utils_1.logger.log(`[Heartbeat] 开始重连,原因: ${reason}`);
|
|
325
325
|
this.lastReconnectTs = Date.now();
|
|
326
326
|
// 重新登录
|
|
327
327
|
if (!await this.signIn()) {
|
|
328
|
-
|
|
328
|
+
utils_1.logger.error('[Heartbeat] 重连失败: signIn 返回 false');
|
|
329
329
|
this.updateStatus('error');
|
|
330
330
|
// 重置 lastHbRecv 避免下次 tick 立即再次触发重连
|
|
331
331
|
this.lastHbRecv = Date.now();
|
|
@@ -337,7 +337,7 @@ class HeartbeatClient {
|
|
|
337
337
|
this.sendFailures = 0;
|
|
338
338
|
this.recvFailures = 0;
|
|
339
339
|
this.lastHbRecv = Date.now();
|
|
340
|
-
|
|
340
|
+
utils_1.logger.log('[Heartbeat] 重连成功');
|
|
341
341
|
this.updateStatus('online');
|
|
342
342
|
// 通知外部组件(如 WebSocket)进行重连
|
|
343
343
|
if (this.onReconnectCallback) {
|
|
@@ -345,13 +345,13 @@ class HeartbeatClient {
|
|
|
345
345
|
this.onReconnectCallback();
|
|
346
346
|
}
|
|
347
347
|
catch (e) {
|
|
348
|
-
|
|
348
|
+
utils_1.logger.error('[Heartbeat] onReconnect 回调异常:', e);
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
return true;
|
|
352
352
|
}
|
|
353
353
|
catch (error) {
|
|
354
|
-
|
|
354
|
+
utils_1.logger.error('[Heartbeat] 重连异常:', error);
|
|
355
355
|
this.updateStatus('error');
|
|
356
356
|
return false;
|
|
357
357
|
}
|
|
@@ -364,7 +364,7 @@ class HeartbeatClient {
|
|
|
364
364
|
*/
|
|
365
365
|
async online() {
|
|
366
366
|
if (this.isRunning) {
|
|
367
|
-
|
|
367
|
+
utils_1.logger.log('[Heartbeat] 已经在线');
|
|
368
368
|
return true;
|
|
369
369
|
}
|
|
370
370
|
this.updateStatus('connecting');
|
|
@@ -422,7 +422,7 @@ class HeartbeatClient {
|
|
|
422
422
|
return [];
|
|
423
423
|
}
|
|
424
424
|
catch (error) {
|
|
425
|
-
|
|
425
|
+
utils_1.logger.error('[Heartbeat] 查询在线状态异常:', error);
|
|
426
426
|
return [];
|
|
427
427
|
}
|
|
428
428
|
}
|