feique 1.5.0 → 1.5.7
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/README.en.md +1 -1
- package/README.md +9 -3
- package/dist/backend/codex.d.ts +1 -0
- package/dist/backend/codex.js +2 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/bridge/commands.d.ts +1 -0
- package/dist/bridge/commands.js +25 -3
- package/dist/bridge/commands.js.map +1 -1
- package/dist/bridge/reply-builders.js +0 -3
- package/dist/bridge/reply-builders.js.map +1 -1
- package/dist/bridge/run-pipeline.js +1 -1
- package/dist/bridge/run-pipeline.js.map +1 -1
- package/dist/bridge/service.d.ts +2 -0
- package/dist/bridge/service.js +117 -46
- package/dist/bridge/service.js.map +1 -1
- package/dist/collaboration/proactive-alerts.d.ts +2 -0
- package/dist/collaboration/proactive-alerts.js +3 -2
- package/dist/collaboration/proactive-alerts.js.map +1 -1
- package/dist/config/init.js +5 -1
- package/dist/config/init.js.map +1 -1
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.js +4 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/extractors.js +3 -0
- package/dist/feishu/extractors.js.map +1 -1
- package/dist/feishu/long-connection.js +2 -1
- package/dist/feishu/long-connection.js.map +1 -1
- package/dist/feishu/webhook.js +2 -1
- package/dist/feishu/webhook.js.map +1 -1
- package/package.json +1 -1
package/dist/bridge/service.js
CHANGED
|
@@ -13,7 +13,7 @@ import { resolveKnowledgeRoots, searchKnowledgeBase } from '../knowledge/search.
|
|
|
13
13
|
import { resolveMessageResources } from '../feishu/message-resource.js';
|
|
14
14
|
import { MemoryStore } from '../state/memory-store.js';
|
|
15
15
|
import { CodexSessionIndex } from '../codex/session-index.js';
|
|
16
|
-
import { resolveProjectBackendWithOverride, resolveProjectBackendName } from '../backend/factory.js';
|
|
16
|
+
import { resolveProjectBackendWithOverride, resolveProjectBackendName, resolveFallbackChain } from '../backend/factory.js';
|
|
17
17
|
import { getBackendDefinition, listBackendNames } from '../backend/registry.js';
|
|
18
18
|
import { buildQueueKey, isExecutionRunStatus, isVisibleRunStatus, mapRunStatusToPhase, buildMessageDedupeKey, buildCardDedupeKey, truncateExcerpt, friendlyErrorMessage, resolveAdminListTarget, buildConversationKeyForConversation, renderMemorySection, formatAgeFromNow, replaceObject, replaceProjects, } from './service-utils.js';
|
|
19
19
|
import { handleDocCommand as handleDocCommandImpl, handleTaskCommand as handleTaskCommandImpl, handleBaseCommand as handleBaseCommandImpl, handleWikiCommand as handleWikiCommandImpl, } from './feishu-commands.js';
|
|
@@ -194,6 +194,7 @@ export class FeiqueService {
|
|
|
194
194
|
return runMaintenanceCycleImpl(this);
|
|
195
195
|
}
|
|
196
196
|
async handleIncomingMessage(context) {
|
|
197
|
+
context = this.normalizeIncomingChatContext(context);
|
|
197
198
|
this.currentMessageContext = context;
|
|
198
199
|
if (!context.text.trim() && context.attachments.length === 0) {
|
|
199
200
|
return;
|
|
@@ -284,7 +285,7 @@ export class FeiqueService {
|
|
|
284
285
|
await this.handleWikiCommand(context, selectionKey, command.action, command.value, command.extra, command.target, command.role);
|
|
285
286
|
return;
|
|
286
287
|
case 'backend':
|
|
287
|
-
await this.handleBackendCommand(context, selectionKey, command.name);
|
|
288
|
+
await this.handleBackendCommand(context, selectionKey, command.name, command.action);
|
|
288
289
|
return;
|
|
289
290
|
case 'session':
|
|
290
291
|
const sessionArgument = command.action === 'adopt' ? command.target : command.threadId;
|
|
@@ -572,7 +573,7 @@ export class FeiqueService {
|
|
|
572
573
|
await this.handleWikiCommand(context, selectionKey, command.action, command.value, command.extra, command.target, command.role);
|
|
573
574
|
return;
|
|
574
575
|
case 'backend':
|
|
575
|
-
await this.handleBackendCommand(context, selectionKey, command.name);
|
|
576
|
+
await this.handleBackendCommand(context, selectionKey, command.name, command.action);
|
|
576
577
|
return;
|
|
577
578
|
case 'session': {
|
|
578
579
|
const sessionArgument = command.action === 'adopt' ? command.target : command.threadId;
|
|
@@ -607,7 +608,7 @@ export class FeiqueService {
|
|
|
607
608
|
openaiImageModel: this.config.service.openai_image_model,
|
|
608
609
|
logger: this.logger,
|
|
609
610
|
});
|
|
610
|
-
if (context.chat_type === 'group' && this.shouldRequireMention(projectContext.project) && context
|
|
611
|
+
if (context.chat_type === 'group' && this.shouldRequireMention(projectContext.project) && !this.messageMentionsBot(context)) {
|
|
611
612
|
return;
|
|
612
613
|
}
|
|
613
614
|
const rateLimitMessage = this.checkAndConsumeChatRateLimit(projectContext.projectAlias, projectContext.project, context.chat_id);
|
|
@@ -1157,8 +1158,41 @@ export class FeiqueService {
|
|
|
1157
1158
|
}
|
|
1158
1159
|
await this.sendTextReply(context.chat_id, adoption.text, context.message_id, context.text);
|
|
1159
1160
|
}
|
|
1160
|
-
async handleBackendCommand(context, selectionKey, name) {
|
|
1161
|
+
async handleBackendCommand(context, selectionKey, name, action) {
|
|
1161
1162
|
const projectContext = await this.resolveProjectContext(context, selectionKey);
|
|
1163
|
+
if (action === 'list') {
|
|
1164
|
+
const sessionOverride = await this.sessionStore.getProjectBackend(projectContext.sessionKey, projectContext.projectAlias);
|
|
1165
|
+
const primaryName = resolveProjectBackendName(this.config, projectContext.projectAlias, sessionOverride);
|
|
1166
|
+
const chain = resolveFallbackChain(this.config, projectContext.projectAlias, primaryName);
|
|
1167
|
+
const project = this.config.projects[projectContext.projectAlias];
|
|
1168
|
+
const failoverEnabled = project?.failover ?? this.config.backend?.failover ?? true;
|
|
1169
|
+
const failoverSource = project?.failover !== undefined
|
|
1170
|
+
? '项目配置'
|
|
1171
|
+
: this.config.backend?.failover !== undefined ? '全局默认' : '注册表默认';
|
|
1172
|
+
const registered = listBackendNames();
|
|
1173
|
+
const lines = [
|
|
1174
|
+
`项目: ${projectContext.projectAlias}`,
|
|
1175
|
+
'',
|
|
1176
|
+
`当前主后端: ${primaryName}${sessionOverride ? '(会话级覆盖)' : project?.backend ? '(项目配置)' : '(全局默认)'}`,
|
|
1177
|
+
`Failover: ${failoverEnabled ? '启用' : '关闭'}(${failoverSource})`,
|
|
1178
|
+
chain.length > 0
|
|
1179
|
+
? `Fallback 链: ${primaryName} → ${chain.join(' → ')}`
|
|
1180
|
+
: `Fallback 链: 无(链为空)`,
|
|
1181
|
+
'',
|
|
1182
|
+
'已注册的后端:',
|
|
1183
|
+
...registered.map((n) => {
|
|
1184
|
+
const marker = n === primaryName ? ' ← 当前' : chain.includes(n) ? ' (fallback)' : '';
|
|
1185
|
+
return ` • ${n}${marker}`;
|
|
1186
|
+
}),
|
|
1187
|
+
'',
|
|
1188
|
+
'用法:',
|
|
1189
|
+
` /backend ${registered.join('|')} — 切换到指定后端`,
|
|
1190
|
+
' /backend — 只查看当前后端',
|
|
1191
|
+
' /backend list — 查看完整清单(本命令)',
|
|
1192
|
+
];
|
|
1193
|
+
await this.sendTextReply(context.chat_id, lines.join('\n'), context.message_id, context.text);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1162
1196
|
if (!name) {
|
|
1163
1197
|
const sessionOverride = await this.sessionStore.getProjectBackend(projectContext.sessionKey, projectContext.projectAlias);
|
|
1164
1198
|
const effectiveName = resolveProjectBackendName(this.config, projectContext.projectAlias, sessionOverride);
|
|
@@ -1484,6 +1518,25 @@ export class FeiqueService {
|
|
|
1484
1518
|
shouldRequireMention(project) {
|
|
1485
1519
|
return project.mention_required || this.config.security.require_group_mentions;
|
|
1486
1520
|
}
|
|
1521
|
+
messageMentionsBot(context) {
|
|
1522
|
+
const botOpenIds = new Set(this.config.feishu.bot_open_ids ?? []);
|
|
1523
|
+
const botName = this.config.feishu.bot_name?.trim();
|
|
1524
|
+
if (botOpenIds.size === 0 && !botName) {
|
|
1525
|
+
return context.mentions.length > 0;
|
|
1526
|
+
}
|
|
1527
|
+
return context.mentions.some((mention) => {
|
|
1528
|
+
if (mention.id && botOpenIds.has(mention.id)) {
|
|
1529
|
+
return true;
|
|
1530
|
+
}
|
|
1531
|
+
return Boolean(botName && mention.name?.trim() === botName);
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
normalizeIncomingChatContext(context) {
|
|
1535
|
+
if (context.chat_type !== 'group' && this.config.feishu.allowed_group_ids.includes(context.chat_id)) {
|
|
1536
|
+
return { ...context, chat_type: 'group' };
|
|
1537
|
+
}
|
|
1538
|
+
return context;
|
|
1539
|
+
}
|
|
1487
1540
|
async cancelActiveRun(queueKey, reason) {
|
|
1488
1541
|
const live = this.activeRuns.get(queueKey);
|
|
1489
1542
|
if (live) {
|
|
@@ -2019,6 +2072,11 @@ export class FeiqueService {
|
|
|
2019
2072
|
}
|
|
2020
2073
|
async sendInitialRunLifecycleReply(input) {
|
|
2021
2074
|
const lifecycleMode = resolveRunLifecycleReplyMode(this.config);
|
|
2075
|
+
// Keep normal runs to a single final reply. Queued runs still get an
|
|
2076
|
+
// explicit queue notice because the user is waiting behind other work.
|
|
2077
|
+
if (!input.queued || lifecycleMode === 'post') {
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2022
2080
|
const draft = this.buildInitialRunLifecycleReply(input.projectAlias, input.queued, lifecycleMode);
|
|
2023
2081
|
try {
|
|
2024
2082
|
const response = await this.sendRunLifecycleReply({
|
|
@@ -2121,48 +2179,61 @@ export class FeiqueService {
|
|
|
2121
2179
|
return false;
|
|
2122
2180
|
}
|
|
2123
2181
|
const sanitizedBody = sanitizeUserVisibleReply(input.body);
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2182
|
+
try {
|
|
2183
|
+
if (target.mode === 'card') {
|
|
2184
|
+
const includeActions = input.runStatus === 'success' && supportsInteractiveCardActions(this.config) && input.sessionKey !== undefined;
|
|
2185
|
+
await this.feishuClient.updateCard(target.messageId, buildRunLifecycleCard({
|
|
2186
|
+
title: input.title,
|
|
2187
|
+
body: input.body,
|
|
2188
|
+
projectAlias: input.projectAlias,
|
|
2189
|
+
runStatus: input.runStatus,
|
|
2190
|
+
runPhase: input.runPhase,
|
|
2191
|
+
cardSummary: input.cardSummary,
|
|
2192
|
+
includeActions,
|
|
2193
|
+
rerunPayload: includeActions && input.sessionKey
|
|
2194
|
+
? {
|
|
2195
|
+
action: 'rerun',
|
|
2196
|
+
conversation_key: input.sessionKey,
|
|
2197
|
+
project_alias: input.projectAlias,
|
|
2198
|
+
chat_id: input.chatId,
|
|
2199
|
+
}
|
|
2200
|
+
: undefined,
|
|
2201
|
+
newSessionPayload: includeActions && input.sessionKey
|
|
2202
|
+
? {
|
|
2203
|
+
action: 'new',
|
|
2204
|
+
conversation_key: input.sessionKey,
|
|
2205
|
+
project_alias: input.projectAlias,
|
|
2206
|
+
chat_id: input.chatId,
|
|
2207
|
+
}
|
|
2208
|
+
: undefined,
|
|
2209
|
+
statusPayload: includeActions && input.sessionKey
|
|
2210
|
+
? {
|
|
2211
|
+
action: 'status',
|
|
2212
|
+
conversation_key: input.sessionKey,
|
|
2213
|
+
project_alias: input.projectAlias,
|
|
2214
|
+
chat_id: input.chatId,
|
|
2215
|
+
}
|
|
2216
|
+
: undefined,
|
|
2217
|
+
}));
|
|
2218
|
+
}
|
|
2219
|
+
else if (target.mode === 'post') {
|
|
2220
|
+
const title = buildReplyTitle(sanitizedBody);
|
|
2221
|
+
await this.feishuClient.updatePost(target.messageId, buildFeishuPost(title, sanitizedBody));
|
|
2222
|
+
}
|
|
2223
|
+
else {
|
|
2224
|
+
await this.feishuClient.updateText(target.messageId, sanitizedBody);
|
|
2225
|
+
}
|
|
2163
2226
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2227
|
+
catch (error) {
|
|
2228
|
+
this.runReplyTargets.delete(input.runId);
|
|
2229
|
+
this.logger.warn({
|
|
2230
|
+
error,
|
|
2231
|
+
chatId: input.chatId,
|
|
2232
|
+
projectAlias: input.projectAlias,
|
|
2233
|
+
runId: input.runId,
|
|
2234
|
+
replyMode: target.mode,
|
|
2235
|
+
}, 'Failed to update run lifecycle reply; will fall back to a new reply');
|
|
2236
|
+
return false;
|
|
2166
2237
|
}
|
|
2167
2238
|
await this.auditLog.append({
|
|
2168
2239
|
type: 'message.updated',
|