evolclaw 3.2.0 → 3.4.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/CHANGELOG.md +53 -0
- package/README.md +7 -4
- package/dist/agents/{resolve.js → baseagent.js} +34 -5
- package/dist/agents/claude-runner.js +120 -31
- package/dist/agents/codex-app-server-client.js +364 -0
- package/dist/agents/codex-runner.js +1152 -140
- package/dist/agents/gemini-runner.js +2 -2
- package/dist/agents/runner-types.js +58 -0
- package/dist/aun/aid/store.js +1 -1
- package/dist/aun/outbox.js +14 -2
- package/dist/aun/storage/download.js +1 -1
- package/dist/aun/storage/upload.js +13 -1
- package/dist/channels/aun.js +869 -358
- package/dist/channels/dingtalk.js +77 -140
- package/dist/channels/feishu.js +125 -154
- package/dist/channels/qqbot.js +75 -138
- package/dist/channels/wechat.js +75 -136
- package/dist/channels/wecom.js +75 -138
- package/dist/cli/agent-command.js +591 -0
- package/dist/cli/agent.js +23 -8
- package/dist/cli/aun-commands.js +1444 -0
- package/dist/cli/ctl-command.js +78 -0
- package/dist/cli/daemon-commands.js +2707 -0
- package/dist/cli/index.js +23 -4905
- package/dist/cli/init.js +33 -6
- package/dist/cli/model.js +1 -1
- package/dist/cli/restart-monitor.js +539 -0
- package/dist/cli/stats.js +558 -0
- package/dist/cli/version.js +87 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +5 -2
- package/dist/config-store.js +12 -6
- package/dist/core/channel-loader.js +88 -83
- package/dist/core/command/command-handler.js +1189 -0
- package/dist/core/command/menu-handler.js +1478 -0
- package/dist/core/command/slash-gate.js +142 -0
- package/dist/core/command/slash-handler.js +2090 -0
- package/dist/core/evolagent-registry.js +82 -0
- package/dist/core/evolagent.js +17 -1
- package/dist/core/interaction-router.js +8 -0
- package/dist/core/message/command-handler-agent-control.js +63 -1
- package/dist/core/message/im-renderer.js +91 -51
- package/dist/core/message/items-formatter.js +9 -1
- package/dist/core/message/message-bridge.js +73 -24
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-processor.js +432 -94
- package/dist/core/message/message-queue.js +70 -2
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-scope.js +2 -2
- package/dist/core/permission.js +25 -12
- package/dist/core/relation/peer-identity.js +16 -1
- package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
- package/dist/core/session/session-manager.js +86 -26
- package/dist/core/session/session-title.js +26 -0
- package/dist/core/stats/billing.js +151 -0
- package/dist/core/stats/budget.js +93 -0
- package/dist/core/stats/db.js +334 -0
- package/dist/core/stats/eck-vars.js +84 -0
- package/dist/core/stats/index.js +10 -0
- package/dist/core/stats/normalizer.js +78 -0
- package/dist/core/stats/query.js +760 -0
- package/dist/core/stats/writer.js +115 -0
- package/dist/core/trigger/manager.js +34 -0
- package/dist/core/trigger/parser.js +9 -3
- package/dist/core/trigger/scheduler.js +20 -17
- package/dist/data/error-dict.json +7 -0
- package/dist/{agents → eck}/manifest-engine.js +20 -1
- package/dist/{agents → eck}/message-renderer.js +24 -1
- package/dist/index.js +174 -9
- package/dist/ipc.js +116 -1
- package/dist/utils/cross-platform.js +58 -5
- package/dist/utils/ecweb-launch.js +49 -0
- package/dist/utils/ecweb-pair.js +20 -0
- package/dist/utils/error-utils.js +18 -5
- package/dist/utils/npm-ops.js +38 -8
- package/dist/utils/stats.js +77 -6
- package/kits/docs/evolclaw/INDEX.md +3 -1
- package/kits/docs/evolclaw/fs-architecture.md +1215 -0
- package/kits/docs/evolclaw/fs.md +131 -0
- package/kits/docs/evolclaw/group-fs.md +209 -0
- package/kits/docs/evolclaw/stats.md +70 -0
- package/kits/docs/venues/aun-group.md +29 -6
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_message_manifest.json +30 -3
- package/kits/rules/05-venue.md +1 -1
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/package.json +5 -6
- package/dist/agents/baseagent-normalize.js +0 -19
- package/dist/core/command-handler.js +0 -3876
- package/dist/core/relation/peer-key.js +0 -16
- package/dist/evolclaw-config.js +0 -11
- package/dist/utils/channel-helpers.js +0 -46
- /package/dist/core/{cache/file-cache.js → daemon-file-cache.js} +0 -0
- /package/dist/{agents → eck}/kit-renderer.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { logger } from '../utils/logger.js';
|
|
2
2
|
import { requireOptional } from '../utils/npm-ops.js';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
|
|
4
4
|
import { formatItemsAsText } from '../core/message/items-formatter.js';
|
|
5
5
|
// ── Webhook SSRF validation ────────────────────────────────────────────────────
|
|
6
6
|
const WEBHOOK_RE = /^https:\/\/(api|oapi)\.dingtalk\.com\//;
|
|
@@ -420,145 +420,82 @@ function isValidCredential(value) {
|
|
|
420
420
|
}
|
|
421
421
|
export class DingtalkChannelPlugin {
|
|
422
422
|
name = 'dingtalk';
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
await channel.sendMessage(channelId, payload.text);
|
|
463
|
-
return;
|
|
464
|
-
case 'result.file':
|
|
465
|
-
await channel.sendFile(channelId, payload.filePath);
|
|
466
|
-
return;
|
|
467
|
-
case 'result.image':
|
|
468
|
-
await channel.sendImage(channelId, payload.data);
|
|
469
|
-
return;
|
|
470
|
-
case 'activity.batch': {
|
|
471
|
-
const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
|
|
472
|
-
const text = formatItemsAsText(filtered);
|
|
473
|
-
if (text)
|
|
474
|
-
await channel.sendMessage(channelId, text);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
case 'interaction':
|
|
478
|
-
if (payload.fallbackText)
|
|
479
|
-
await channel.sendMessage(channelId, payload.fallbackText);
|
|
480
|
-
return;
|
|
481
|
-
case 'status.started':
|
|
482
|
-
case 'status.completed':
|
|
483
|
-
case 'status.interrupted':
|
|
484
|
-
case 'status.error':
|
|
485
|
-
case 'status.timeout':
|
|
486
|
-
case 'status.progress':
|
|
487
|
-
case 'custom':
|
|
488
|
-
return;
|
|
489
|
-
default:
|
|
490
|
-
logger.warn(`[DingTalk] Unhandled payload kind: ${payload.kind}`);
|
|
423
|
+
async createInstance(inst, ctx) {
|
|
424
|
+
if (inst.enabled === false)
|
|
425
|
+
return null;
|
|
426
|
+
if (!isValidCredential(inst.clientId) || !isValidCredential(inst.clientSecret))
|
|
427
|
+
return null;
|
|
428
|
+
const channel = new DingtalkChannel({
|
|
429
|
+
clientId: inst.clientId,
|
|
430
|
+
clientSecret: inst.clientSecret,
|
|
431
|
+
requireMention: inst.requireMention,
|
|
432
|
+
freeResponseChats: inst.freeResponseChats,
|
|
433
|
+
});
|
|
434
|
+
const mode = resolveShowActivities(inst);
|
|
435
|
+
const adapter = {
|
|
436
|
+
channelName: inst.name,
|
|
437
|
+
channelKey: inst.name,
|
|
438
|
+
capabilities: { file: true, image: true, interaction: false, markdown: true, thought: false, status: false, thread: false },
|
|
439
|
+
send: async (envelope, payload) => {
|
|
440
|
+
const channelId = envelope.channelId;
|
|
441
|
+
switch (payload.kind) {
|
|
442
|
+
case 'result.text':
|
|
443
|
+
case 'command.result':
|
|
444
|
+
case 'command.error':
|
|
445
|
+
case 'system.notice':
|
|
446
|
+
case 'system.error':
|
|
447
|
+
case 'result.error':
|
|
448
|
+
await channel.sendMessage(channelId, payload.text);
|
|
449
|
+
return;
|
|
450
|
+
case 'result.file':
|
|
451
|
+
await channel.sendFile(channelId, payload.filePath);
|
|
452
|
+
return;
|
|
453
|
+
case 'result.image':
|
|
454
|
+
await channel.sendImage(channelId, payload.data);
|
|
455
|
+
return;
|
|
456
|
+
case 'activity.batch': {
|
|
457
|
+
const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
|
|
458
|
+
const text = formatItemsAsText(filtered);
|
|
459
|
+
if (text)
|
|
460
|
+
await channel.sendMessage(channelId, text);
|
|
461
|
+
return;
|
|
491
462
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
channelType: 'dingtalk',
|
|
530
|
-
adapter,
|
|
531
|
-
channel,
|
|
532
|
-
policy,
|
|
533
|
-
options,
|
|
534
|
-
connect: () => channel.connect(),
|
|
535
|
-
disconnect: () => channel.disconnect(),
|
|
536
|
-
onProjectPathRequest: () => Promise.resolve(config.projects?.defaultPath || process.cwd()),
|
|
537
|
-
registerBridge(bridge, channelType) {
|
|
538
|
-
bridge.register(adapter.channelName, (handler) => channel.onMessage(async (event) => {
|
|
539
|
-
await handler({
|
|
540
|
-
channel: adapter.channelName,
|
|
541
|
-
channelType,
|
|
542
|
-
channelId: event.channelId,
|
|
543
|
-
selfAID: inst.agentName,
|
|
544
|
-
content: event.content,
|
|
545
|
-
images: event.images,
|
|
546
|
-
chatType: event.chatType || 'private',
|
|
547
|
-
peerId: event.peerId || '',
|
|
548
|
-
peerName: event.peerName,
|
|
549
|
-
messageId: event.messageId,
|
|
550
|
-
});
|
|
551
|
-
}), (channelId, text) => channel.sendMessage(channelId, text), adapter, channelType);
|
|
552
|
-
},
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
return result;
|
|
556
|
-
}
|
|
557
|
-
async createChannel(config) {
|
|
558
|
-
const instances = await this.createChannels(config);
|
|
559
|
-
if (instances.length === 0) {
|
|
560
|
-
throw new Error('DingTalk config missing or invalid');
|
|
561
|
-
}
|
|
562
|
-
return instances[0];
|
|
463
|
+
case 'interaction':
|
|
464
|
+
if (payload.fallbackText)
|
|
465
|
+
await channel.sendMessage(channelId, payload.fallbackText);
|
|
466
|
+
return;
|
|
467
|
+
default: return;
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
const policy = {
|
|
472
|
+
canSwitchProject: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
473
|
+
canListProjects: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
474
|
+
canCreateSession: () => true,
|
|
475
|
+
canDeleteSession: () => true,
|
|
476
|
+
canImportCliSession: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
477
|
+
messagePrefix: (chatType, peerName) => (chatType === 'group' && peerName) ? `[${peerName}] ` : '',
|
|
478
|
+
showMiddleResult: (chatType, identity) => showActivitiesPolicy(mode, chatType, identity),
|
|
479
|
+
showIdleMonitor: (chatType, identity) => showActivitiesPolicy(mode, chatType, identity),
|
|
480
|
+
accumulateErrors: () => true,
|
|
481
|
+
};
|
|
482
|
+
return {
|
|
483
|
+
channelType: 'dingtalk', adapter, channel,
|
|
484
|
+
policy,
|
|
485
|
+
options: { fileMarkerPattern: /\[SEND_FILE:(?:(\w+):)?([^\]]+)\]/g, supportsImages: true, flushDelay: inst.flushDelay },
|
|
486
|
+
connect: () => channel.connect(),
|
|
487
|
+
disconnect: () => channel.disconnect(),
|
|
488
|
+
onProjectPathRequest: () => Promise.resolve(ctx.defaultProjectPath),
|
|
489
|
+
registerBridge(bridge, channelType) {
|
|
490
|
+
bridge.register(adapter.channelName, (handler) => channel.onMessage(async (event) => {
|
|
491
|
+
await handler({
|
|
492
|
+
channel: adapter.channelName, channelType, channelId: event.channelId,
|
|
493
|
+
selfAID: ctx.agentName, content: event.content, images: event.images,
|
|
494
|
+
chatType: event.chatType || 'private', peerId: event.peerId || '',
|
|
495
|
+
peerName: event.peerName, messageId: event.messageId,
|
|
496
|
+
});
|
|
497
|
+
}), (channelId, text) => channel.sendMessage(channelId, text), adapter, channelType);
|
|
498
|
+
},
|
|
499
|
+
};
|
|
563
500
|
}
|
|
564
501
|
}
|
package/dist/channels/feishu.js
CHANGED
|
@@ -365,6 +365,8 @@ export class FeishuChannel {
|
|
|
365
365
|
}
|
|
366
366
|
catch (err) {
|
|
367
367
|
logger.error('[Feishu] Failed to handle card action:', err);
|
|
368
|
+
const detail = err instanceof Error && err.message ? err.message : String(err || '未知错误');
|
|
369
|
+
return { toast: { type: 'error', content: `❌ 操作失败: ${detail}` } };
|
|
368
370
|
}
|
|
369
371
|
},
|
|
370
372
|
});
|
|
@@ -1038,15 +1040,36 @@ export class FeishuChannel {
|
|
|
1038
1040
|
return false;
|
|
1039
1041
|
}
|
|
1040
1042
|
}
|
|
1041
|
-
|
|
1043
|
+
// messageId → Pin reaction create promise(等待 reaction_id 落地,供 promoteAck 删除)
|
|
1044
|
+
pinReactions = new Map();
|
|
1045
|
+
/** 收到消息时添加 Pin 表情(表示"已收到,排队中") */
|
|
1046
|
+
addPinReaction(messageId) {
|
|
1042
1047
|
if (!this.client)
|
|
1043
1048
|
return;
|
|
1049
|
+
const p = this.client.im.messageReaction.create({
|
|
1050
|
+
path: { message_id: messageId },
|
|
1051
|
+
data: { reaction_type: { emoji_type: 'Pin' } },
|
|
1052
|
+
}).then((res) => res?.data?.reaction_id)
|
|
1053
|
+
.catch(() => undefined);
|
|
1054
|
+
this.pinReactions.set(messageId, p);
|
|
1055
|
+
}
|
|
1056
|
+
/** Runner 开始执行时:添加 CheckMark,然后异步移除 Pin(视觉无闪烁) */
|
|
1057
|
+
async promoteAckReaction(messageId) {
|
|
1058
|
+
if (!this.client)
|
|
1059
|
+
return;
|
|
1060
|
+
// 先加 CheckMark,再删 Pin——用户看到的是 Pin→Pin+CheckMark→CheckMark,无空窗
|
|
1044
1061
|
this.client.im.messageReaction.create({
|
|
1045
1062
|
path: { message_id: messageId },
|
|
1046
|
-
data: {
|
|
1047
|
-
reaction_type: { emoji_type: 'CheckMark' }
|
|
1048
|
-
}
|
|
1063
|
+
data: { reaction_type: { emoji_type: 'CheckMark' } },
|
|
1049
1064
|
}).catch(() => { });
|
|
1065
|
+
const pending = this.pinReactions.get(messageId);
|
|
1066
|
+
this.pinReactions.delete(messageId);
|
|
1067
|
+
const reactionId = await pending;
|
|
1068
|
+
if (reactionId) {
|
|
1069
|
+
this.client.im.messageReaction.delete({
|
|
1070
|
+
path: { message_id: messageId, reaction_id: reactionId },
|
|
1071
|
+
}).catch(() => { });
|
|
1072
|
+
}
|
|
1050
1073
|
}
|
|
1051
1074
|
}
|
|
1052
1075
|
export class CardMetaStore {
|
|
@@ -1389,159 +1412,107 @@ export function hasMarkdownSyntax(text) {
|
|
|
1389
1412
|
];
|
|
1390
1413
|
return markdownPatterns.some(pattern => pattern.test(text));
|
|
1391
1414
|
}
|
|
1392
|
-
import {
|
|
1415
|
+
import { resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
|
|
1393
1416
|
import { resolvePaths } from '../paths.js';
|
|
1394
1417
|
export class FeishuChannelPlugin {
|
|
1395
1418
|
name = 'feishu';
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
case 'result.text':
|
|
1428
|
-
case 'command.result':
|
|
1429
|
-
case 'command.error':
|
|
1430
|
-
case 'system.notice':
|
|
1431
|
-
case 'system.error':
|
|
1432
|
-
case 'result.error': {
|
|
1433
|
-
const sendCtx = { ...(ctx ?? {}) };
|
|
1434
|
-
if (payload.kind === 'result.text' && payload.isFinal)
|
|
1435
|
-
sendCtx.title = '✅ 最终回复:';
|
|
1436
|
-
if (ctx?.metadata?.onThreadCreated)
|
|
1437
|
-
sendCtx.onThreadCreated = ctx.metadata.onThreadCreated;
|
|
1438
|
-
await channel.sendMessage(channelId, payload.text, sendCtx);
|
|
1439
|
-
return;
|
|
1440
|
-
}
|
|
1441
|
-
case 'result.file':
|
|
1442
|
-
await channel.sendFile(channelId, payload.filePath, ctx);
|
|
1443
|
-
return;
|
|
1444
|
-
case 'result.image':
|
|
1445
|
-
await channel.sendImage(channelId, payload.data, ctx);
|
|
1446
|
-
return;
|
|
1447
|
-
case 'activity.batch': {
|
|
1448
|
-
// Feishu 不发送成功的 tool_result(信息密度低,刷屏)
|
|
1449
|
-
const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
|
|
1450
|
-
const text = formatItemsAsText(filtered);
|
|
1451
|
-
if (text) {
|
|
1452
|
-
await channel.sendMessage(channelId, text, ctx);
|
|
1453
|
-
}
|
|
1454
|
-
return;
|
|
1455
|
-
}
|
|
1456
|
-
case 'status.started':
|
|
1457
|
-
case 'status.completed':
|
|
1458
|
-
case 'status.interrupted':
|
|
1459
|
-
case 'status.error':
|
|
1460
|
-
case 'status.timeout':
|
|
1461
|
-
case 'status.progress':
|
|
1462
|
-
// Feishu 通过 acknowledge (✓ 表情) 表达状态,由 channel 自行处理
|
|
1463
|
-
return;
|
|
1464
|
-
case 'interaction': {
|
|
1465
|
-
const sent = await channel.sendInteraction(channelId, payload.interaction, ctx);
|
|
1466
|
-
if (!sent)
|
|
1467
|
-
throw new Error('sendInteraction returned false');
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
case 'custom':
|
|
1471
|
-
// Feishu 不支持自定义 payload
|
|
1472
|
-
return;
|
|
1473
|
-
default:
|
|
1474
|
-
logger.warn(`[Feishu] Unhandled payload kind: ${payload.kind}`);
|
|
1419
|
+
async createInstance(inst, ctx) {
|
|
1420
|
+
if (inst.enabled === false || !inst.appId || !inst.appSecret)
|
|
1421
|
+
return null;
|
|
1422
|
+
const channel = new FeishuChannel({
|
|
1423
|
+
appId: inst.appId,
|
|
1424
|
+
appSecret: inst.appSecret,
|
|
1425
|
+
enableRichContent: ctx.enableRichContent,
|
|
1426
|
+
seenMsgFile: path.join(resolvePaths().dataDir, `feishu-seen-${inst.name}.jsonl`),
|
|
1427
|
+
});
|
|
1428
|
+
const mode = resolveShowActivities(inst);
|
|
1429
|
+
const adapter = {
|
|
1430
|
+
channelName: inst.name,
|
|
1431
|
+
channelKey: inst.name,
|
|
1432
|
+
capabilities: { file: true, image: true, interaction: true, markdown: true, thought: false, status: true, thread: true },
|
|
1433
|
+
send: async (envelope, payload) => {
|
|
1434
|
+
const replyCtx = envelope.replyContext;
|
|
1435
|
+
const channelId = envelope.channelId;
|
|
1436
|
+
switch (payload.kind) {
|
|
1437
|
+
case 'result.text':
|
|
1438
|
+
case 'command.result':
|
|
1439
|
+
case 'command.error':
|
|
1440
|
+
case 'system.notice':
|
|
1441
|
+
case 'system.error':
|
|
1442
|
+
case 'result.error': {
|
|
1443
|
+
const sendCtx = { ...(replyCtx ?? {}) };
|
|
1444
|
+
if (payload.kind === 'result.text' && payload.isFinal)
|
|
1445
|
+
sendCtx.title = '✅ 最终回复:';
|
|
1446
|
+
if (replyCtx?.metadata?.onThreadCreated)
|
|
1447
|
+
sendCtx.onThreadCreated = replyCtx.metadata.onThreadCreated;
|
|
1448
|
+
await channel.sendMessage(channelId, payload.text, sendCtx);
|
|
1449
|
+
return;
|
|
1475
1450
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
replyInThread:
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
if (instances.length === 0) {
|
|
1543
|
-
throw new Error('Feishu config missing');
|
|
1544
|
-
}
|
|
1545
|
-
return instances[0];
|
|
1451
|
+
case 'result.file':
|
|
1452
|
+
await channel.sendFile(channelId, payload.filePath, replyCtx);
|
|
1453
|
+
return;
|
|
1454
|
+
case 'result.image':
|
|
1455
|
+
await channel.sendImage(channelId, payload.data, replyCtx);
|
|
1456
|
+
return;
|
|
1457
|
+
case 'activity.batch': {
|
|
1458
|
+
const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
|
|
1459
|
+
const text = formatItemsAsText(filtered);
|
|
1460
|
+
if (text)
|
|
1461
|
+
await channel.sendMessage(channelId, text, replyCtx);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
case 'status.started':
|
|
1465
|
+
case 'status.completed':
|
|
1466
|
+
case 'status.interrupted':
|
|
1467
|
+
case 'status.error':
|
|
1468
|
+
case 'status.timeout':
|
|
1469
|
+
case 'status.progress': return;
|
|
1470
|
+
case 'interaction': {
|
|
1471
|
+
const sent = await channel.sendInteraction(channelId, payload.interaction, replyCtx);
|
|
1472
|
+
if (!sent)
|
|
1473
|
+
throw new Error('sendInteraction returned false');
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
case 'custom': return;
|
|
1477
|
+
default: logger.warn(`[Feishu] Unhandled payload kind: ${payload.kind}`);
|
|
1478
|
+
}
|
|
1479
|
+
},
|
|
1480
|
+
acknowledge: (messageId) => { channel.addPinReaction(messageId); return Promise.resolve(); },
|
|
1481
|
+
promoteAck: (messageId) => channel.promoteAckReaction(messageId),
|
|
1482
|
+
onInteraction: (callback) => channel.onInteraction(callback),
|
|
1483
|
+
};
|
|
1484
|
+
const policy = {
|
|
1485
|
+
canSwitchProject: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
1486
|
+
canListProjects: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
1487
|
+
canCreateSession: () => true,
|
|
1488
|
+
canDeleteSession: () => true,
|
|
1489
|
+
canImportCliSession: (_, identity) => identity === 'owner' || identity === 'admin',
|
|
1490
|
+
messagePrefix: (chatType, peerName) => (chatType === 'group' && peerName) ? `[${peerName}] ` : '',
|
|
1491
|
+
showMiddleResult: (chatType, identity) => showActivitiesPolicy(mode, chatType, identity),
|
|
1492
|
+
showIdleMonitor: (chatType, identity) => showActivitiesPolicy(mode, chatType, identity),
|
|
1493
|
+
accumulateErrors: () => true,
|
|
1494
|
+
};
|
|
1495
|
+
return {
|
|
1496
|
+
channelType: 'feishu', adapter, channel,
|
|
1497
|
+
policy,
|
|
1498
|
+
options: { fileMarkerPattern: /\[SEND_FILE:(?:(\w+):)?([^\]]+)\]/g, supportsImages: true, flushDelay: inst.flushDelay },
|
|
1499
|
+
connect: () => channel.connect(),
|
|
1500
|
+
disconnect: () => channel.disconnect(),
|
|
1501
|
+
onProjectPathRequest: () => Promise.resolve(ctx.defaultProjectPath),
|
|
1502
|
+
registerBridge(bridge, channelType) {
|
|
1503
|
+
bridge.register(adapter.channelName, (handler) => channel.onMessage(async ({ channelId: chatId, content, images, peerId, peerName, messageId, mentions, mentionAids, threadId, rootId, chatType, source }) => {
|
|
1504
|
+
await handler({
|
|
1505
|
+
channel: adapter.channelName, channelType, channelId: chatId, content, images,
|
|
1506
|
+
selfAID: ctx.agentName, chatType: chatType || 'private',
|
|
1507
|
+
peerId: peerId || '', peerName, messageId, mentions, mentionAids, threadId,
|
|
1508
|
+
replyContext: threadId ? { replyToMessageId: rootId ?? threadId, replyInThread: true } : undefined,
|
|
1509
|
+
source,
|
|
1510
|
+
});
|
|
1511
|
+
}), (channelId, text, replyContext) => channel.sendMessage(channelId, text, {
|
|
1512
|
+
replyToMessageId: replyContext?.replyToMessageId,
|
|
1513
|
+
replyInThread: replyContext?.replyInThread,
|
|
1514
|
+
}), adapter, channelType);
|
|
1515
|
+
},
|
|
1516
|
+
};
|
|
1546
1517
|
}
|
|
1547
1518
|
}
|