metame-cli 1.5.19 → 1.5.21
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/index.js +157 -80
- package/package.json +2 -2
- package/scripts/bin/bootstrap-worktree.sh +20 -0
- package/scripts/core/audit.js +190 -0
- package/scripts/core/handoff.js +780 -0
- package/scripts/core/handoff.test.js +1074 -0
- package/scripts/core/memory-model.js +183 -0
- package/scripts/core/memory-model.test.js +486 -0
- package/scripts/core/reactive-paths.js +44 -0
- package/scripts/core/reactive-paths.test.js +35 -0
- package/scripts/core/reactive-prompt.js +51 -0
- package/scripts/core/reactive-prompt.test.js +88 -0
- package/scripts/core/reactive-signal.js +40 -0
- package/scripts/core/reactive-signal.test.js +88 -0
- package/scripts/core/thread-chat-id.js +52 -0
- package/scripts/core/thread-chat-id.test.js +113 -0
- package/scripts/daemon-bridges.js +92 -38
- package/scripts/daemon-claude-engine.js +373 -444
- package/scripts/daemon-command-router.js +82 -8
- package/scripts/daemon-engine-runtime.js +7 -10
- package/scripts/daemon-reactive-lifecycle.js +100 -33
- package/scripts/daemon-session-commands.js +133 -43
- package/scripts/daemon-session-store.js +300 -82
- package/scripts/daemon-team-dispatch.js +16 -16
- package/scripts/daemon.js +21 -175
- package/scripts/deploy-manifest.js +90 -0
- package/scripts/docs/maintenance-manual.md +14 -11
- package/scripts/docs/pointer-map.md +13 -4
- package/scripts/feishu-adapter.js +31 -27
- package/scripts/hooks/intent-engine.js +6 -3
- package/scripts/hooks/intent-memory-recall.js +1 -0
- package/scripts/hooks/intent-perpetual.js +1 -1
- package/scripts/memory-extract.js +5 -97
- package/scripts/memory-gc.js +35 -90
- package/scripts/memory-migrate-v2.js +304 -0
- package/scripts/memory-nightly-reflect.js +40 -41
- package/scripts/memory.js +340 -859
- package/scripts/migrate-reactive-paths.js +122 -0
- package/scripts/signal-capture.js +4 -0
- package/scripts/sync-plugin.js +56 -0
|
@@ -4,9 +4,12 @@ let userAcl = null;
|
|
|
4
4
|
try { userAcl = require('./daemon-user-acl'); } catch { /* optional */ }
|
|
5
5
|
const { findTeamMember: _findTeamMember } = require('./daemon-team-dispatch');
|
|
6
6
|
const { isRemoteMember } = require('./daemon-remote-dispatch');
|
|
7
|
+
const { buildThreadChatId, isThreadChatId, rawChatId: _threadRawChatId } = require('./core/thread-chat-id');
|
|
7
8
|
const imessageIO = (() => { try { return require('./daemon-siri-imessage'); } catch { return null; } })();
|
|
8
9
|
const siriBridgeMod = (() => { try { return require('./daemon-siri-bridge'); } catch { return null; } })();
|
|
9
10
|
const weixinBridgeMod = (() => { try { return require('./daemon-weixin-bridge'); } catch { return null; } })();
|
|
11
|
+
const MSG_SESSION_MAX_ENTRIES = 5000;
|
|
12
|
+
const MSG_SESSION_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1000;
|
|
10
13
|
|
|
11
14
|
function createBridgeStarter(deps) {
|
|
12
15
|
const {
|
|
@@ -114,6 +117,20 @@ function createBridgeStarter(deps) {
|
|
|
114
117
|
return null;
|
|
115
118
|
}
|
|
116
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Extract the topic root message ID (root_id) from a Feishu event.
|
|
122
|
+
* Returns non-null ONLY for messages inside a Feishu "话题" thread,
|
|
123
|
+
* NOT for plain quoted replies in conversation mode.
|
|
124
|
+
*/
|
|
125
|
+
function extractFeishuThreadRootId(event) {
|
|
126
|
+
const msg = (event && event.message) || (event && event.event && event.event.message);
|
|
127
|
+
if (!msg) return null;
|
|
128
|
+
// root_id is set when the message belongs to a topic thread.
|
|
129
|
+
// In conversation mode, a simple "指定回复" sets parent_id but NOT root_id.
|
|
130
|
+
const rootId = String(msg.root_id || '').trim();
|
|
131
|
+
return rootId || null;
|
|
132
|
+
}
|
|
133
|
+
|
|
117
134
|
function trackBridgeReplyMapping(messageId, payload = {}) {
|
|
118
135
|
const safeMessageId = String(messageId || '').trim();
|
|
119
136
|
if (!safeMessageId) return;
|
|
@@ -122,7 +139,20 @@ function createBridgeStarter(deps) {
|
|
|
122
139
|
state.msg_sessions[safeMessageId] = {
|
|
123
140
|
...(state.msg_sessions[safeMessageId] || {}),
|
|
124
141
|
...payload,
|
|
142
|
+
touchedAt: Date.now(),
|
|
125
143
|
};
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const entries = Object.entries(state.msg_sessions).filter(([, value]) => {
|
|
146
|
+
const touchedAt = Number(value && value.touchedAt || 0);
|
|
147
|
+
return !touchedAt || (now - touchedAt) <= MSG_SESSION_MAX_AGE_MS;
|
|
148
|
+
});
|
|
149
|
+
state.msg_sessions = Object.fromEntries(
|
|
150
|
+
(entries.length > MSG_SESSION_MAX_ENTRIES
|
|
151
|
+
? entries
|
|
152
|
+
.sort((a, b) => Number((a[1] && a[1].touchedAt) || 0) - Number((b[1] && b[1].touchedAt) || 0))
|
|
153
|
+
.slice(entries.length - MSG_SESSION_MAX_ENTRIES)
|
|
154
|
+
: entries)
|
|
155
|
+
);
|
|
126
156
|
saveState(state);
|
|
127
157
|
}
|
|
128
158
|
|
|
@@ -160,10 +190,9 @@ function createBridgeStarter(deps) {
|
|
|
160
190
|
const map = {
|
|
161
191
|
...(cfg.telegram ? cfg.telegram.chat_agent_map || {} : {}),
|
|
162
192
|
...(cfg.feishu ? cfg.feishu.chat_agent_map || {} : {}),
|
|
163
|
-
...(cfg.weixin ? cfg.weixin.chat_agent_map || {} : {}),
|
|
164
193
|
...(cfg.imessage ? cfg.imessage.chat_agent_map || {} : {}),
|
|
165
194
|
};
|
|
166
|
-
const key = map[String(chatId)];
|
|
195
|
+
const key = map[String(chatId)] || map[_threadRawChatId(chatId)];
|
|
167
196
|
const proj = key && cfg.projects ? cfg.projects[key] : null;
|
|
168
197
|
return { key: key || null, project: proj || null };
|
|
169
198
|
}
|
|
@@ -181,9 +210,9 @@ function createBridgeStarter(deps) {
|
|
|
181
210
|
},
|
|
182
211
|
});
|
|
183
212
|
}
|
|
184
|
-
// Get team member's working directory
|
|
185
|
-
// Creates agents/<key>/ directory
|
|
186
|
-
function _getMemberCwd(parentCwd, key) {
|
|
213
|
+
// Get team member's working directory inside the source tree, never under ~/.metame.
|
|
214
|
+
// Creates agents/<key>/ directory by default, or ensures an explicit member.cwd exists.
|
|
215
|
+
function _getMemberCwd(parentCwd, key, explicitCwd = null) {
|
|
187
216
|
const { existsSync, mkdirSync, symlinkSync, readFileSync, writeFileSync } = require('fs');
|
|
188
217
|
const { execFileSync } = require('child_process');
|
|
189
218
|
const WIN_HIDE = process.platform === 'win32' ? { windowsHide: true } : {};
|
|
@@ -194,12 +223,14 @@ function createBridgeStarter(deps) {
|
|
|
194
223
|
log('WARN', `Sanitized team member key: ${key} -> ${safeKey}`);
|
|
195
224
|
}
|
|
196
225
|
|
|
197
|
-
// Use
|
|
226
|
+
// Use explicit member cwd when provided, otherwise default to agents/<key>/.
|
|
198
227
|
const agentsDir = path.join(parentCwd, 'agents');
|
|
199
|
-
const memberDir =
|
|
228
|
+
const memberDir = explicitCwd
|
|
229
|
+
? path.resolve(String(explicitCwd).replace(/^~/, require('os').homedir()))
|
|
230
|
+
: path.join(agentsDir, safeKey);
|
|
200
231
|
|
|
201
|
-
// Create agents directory if
|
|
202
|
-
if (!existsSync(agentsDir)) {
|
|
232
|
+
// Create agents directory if using the default layout.
|
|
233
|
+
if (!explicitCwd && !existsSync(agentsDir)) {
|
|
203
234
|
mkdirSync(agentsDir, { recursive: true });
|
|
204
235
|
}
|
|
205
236
|
|
|
@@ -279,12 +310,19 @@ function createBridgeStarter(deps) {
|
|
|
279
310
|
return;
|
|
280
311
|
}
|
|
281
312
|
|
|
282
|
-
|
|
313
|
+
// When dispatching from a topic thread, include the thread ID in the
|
|
314
|
+
// virtual session key so each topic gets its own independent session.
|
|
315
|
+
const realChatIdStr = String(realChatId || '');
|
|
316
|
+
const virtualChatId = isThreadChatId(realChatIdStr)
|
|
317
|
+
? `_agent_${member.key}::${realChatIdStr}`
|
|
318
|
+
: `_agent_${member.key}`;
|
|
283
319
|
const parentCwd = member.cwd || boundProj.cwd;
|
|
284
320
|
const resolvedParentCwd = parentCwd.replace(/^~/, require('os').homedir());
|
|
285
|
-
const memberCwd =
|
|
286
|
-
|
|
287
|
-
|
|
321
|
+
const memberCwd = _getMemberCwd(
|
|
322
|
+
resolvedParentCwd,
|
|
323
|
+
member.key,
|
|
324
|
+
member.cwd || null,
|
|
325
|
+
);
|
|
288
326
|
if (!memberCwd) {
|
|
289
327
|
log('ERROR', `Team [${member.key}] cannot start: directory unavailable`);
|
|
290
328
|
bot.sendMessage(realChatId, `❌ ${member.icon || '🤖'} ${member.name} 启动失败:工作目录创建失败`).catch(() => {});
|
|
@@ -710,6 +748,13 @@ function createBridgeStarter(deps) {
|
|
|
710
748
|
return;
|
|
711
749
|
}
|
|
712
750
|
|
|
751
|
+
// ── Topic mode detection (before file/text split) ──
|
|
752
|
+
const threadRootId = extractFeishuThreadRootId(event);
|
|
753
|
+
const pipelineChatId = threadRootId ? buildThreadChatId(chatId, threadRootId) : chatId;
|
|
754
|
+
if (threadRootId) {
|
|
755
|
+
log('INFO', `Feishu topic detected: root=${threadRootId} → pipelineChatId=${pipelineChatId}`);
|
|
756
|
+
}
|
|
757
|
+
|
|
713
758
|
if (fileInfo && fileInfo.fileKey) {
|
|
714
759
|
const acl = await applyUserAcl({
|
|
715
760
|
bot,
|
|
@@ -721,7 +766,7 @@ function createBridgeStarter(deps) {
|
|
|
721
766
|
});
|
|
722
767
|
if (acl.blocked) return;
|
|
723
768
|
log('INFO', `Feishu file from ${chatId}: ${fileInfo.fileName} (key: ${fileInfo.fileKey}, msgId: ${fileInfo.messageId}, type: ${fileInfo.msgType})`);
|
|
724
|
-
const session = getSession(chatId);
|
|
769
|
+
const session = getSession(pipelineChatId) || getSession(chatId);
|
|
725
770
|
const cwd = session?.cwd || HOME;
|
|
726
771
|
const uploadDir = path.join(cwd, 'upload');
|
|
727
772
|
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
|
|
@@ -729,7 +774,7 @@ function createBridgeStarter(deps) {
|
|
|
729
774
|
|
|
730
775
|
try {
|
|
731
776
|
await bot.downloadFile(fileInfo.messageId, fileInfo.fileKey, destPath, fileInfo.msgType);
|
|
732
|
-
await bot.sendMessage(
|
|
777
|
+
await bot.sendMessage(pipelineChatId, `📥 Saved: ${fileInfo.fileName}`);
|
|
733
778
|
|
|
734
779
|
const prompt = text
|
|
735
780
|
? `User uploaded a file to the project: ${destPath}\nUser says: "${text}"`
|
|
@@ -737,21 +782,21 @@ function createBridgeStarter(deps) {
|
|
|
737
782
|
|
|
738
783
|
// Respect team_sticky: route to active agent same as text messages
|
|
739
784
|
const _stFile = loadState();
|
|
740
|
-
const _chatKeyFile = String(
|
|
785
|
+
const _chatKeyFile = String(pipelineChatId);
|
|
741
786
|
const { project: _boundProjFile } = _getBoundProject(chatId, liveCfg);
|
|
742
787
|
const _stickyKeyFile = (_stFile.team_sticky || {})[_chatKeyFile];
|
|
743
788
|
if (_boundProjFile && Array.isArray(_boundProjFile.team) && _boundProjFile.team.length > 0 && _stickyKeyFile) {
|
|
744
789
|
const _stickyMember = _boundProjFile.team.find(m => m.key === _stickyKeyFile);
|
|
745
790
|
if (_stickyMember) {
|
|
746
791
|
log('INFO', `Feishu file → sticky route to ${_stickyKeyFile}`);
|
|
747
|
-
_dispatchToTeamMember(_stickyMember, _boundProjFile, prompt, liveCfg, bot,
|
|
792
|
+
_dispatchToTeamMember(_stickyMember, _boundProjFile, prompt, liveCfg, bot, pipelineChatId, executeTaskByName, acl);
|
|
748
793
|
return;
|
|
749
794
|
}
|
|
750
795
|
}
|
|
751
|
-
await pipeline.processMessage(
|
|
796
|
+
await pipeline.processMessage(pipelineChatId, prompt, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
|
|
752
797
|
} catch (err) {
|
|
753
798
|
log('ERROR', `Feishu file download failed: ${err.message}`);
|
|
754
|
-
await bot.sendMessage(
|
|
799
|
+
await bot.sendMessage(pipelineChatId, `❌ Download failed: ${err.message}`);
|
|
755
800
|
}
|
|
756
801
|
return;
|
|
757
802
|
}
|
|
@@ -772,10 +817,13 @@ function createBridgeStarter(deps) {
|
|
|
772
817
|
let _replyMappingFound = false; // true = mapping exists (agentKey may be null = main)
|
|
773
818
|
// Load state once for the entire routing block
|
|
774
819
|
const _st = loadState();
|
|
775
|
-
|
|
820
|
+
// Quoted reply = explicit parentId but NOT a topic thread (topics always carry parentId=root_id)
|
|
821
|
+
const _isQuotedReply = !!(parentId && !threadRootId);
|
|
822
|
+
if (_isQuotedReply) {
|
|
776
823
|
log('INFO', `Feishu reply metadata detected chat=${chatId} parentId=${parentId}`);
|
|
777
824
|
}
|
|
778
|
-
|
|
825
|
+
// In topic mode, session continuity is handled by pipelineChatId — skip msg_sessions lookup
|
|
826
|
+
if (_isQuotedReply) {
|
|
779
827
|
const mapped = _st.msg_sessions && _st.msg_sessions[parentId];
|
|
780
828
|
if (mapped) {
|
|
781
829
|
_replyMappingFound = true;
|
|
@@ -798,7 +846,8 @@ function createBridgeStarter(deps) {
|
|
|
798
846
|
}
|
|
799
847
|
|
|
800
848
|
// Helper: set/clear sticky on shared state object and persist
|
|
801
|
-
|
|
849
|
+
// Use pipelineChatId so each topic gets independent sticky state
|
|
850
|
+
const _chatKey = String(pipelineChatId);
|
|
802
851
|
const _setSticky = (key) => {
|
|
803
852
|
if (!_st.team_sticky) _st.team_sticky = {};
|
|
804
853
|
_st.team_sticky[_chatKey] = key;
|
|
@@ -836,21 +885,23 @@ function createBridgeStarter(deps) {
|
|
|
836
885
|
// Priority 3: bare /stop → sticky
|
|
837
886
|
if (!_targetKey && !_stopArg) _targetKey = _stickyKey;
|
|
838
887
|
if (_targetKey) {
|
|
839
|
-
const vid =
|
|
888
|
+
const vid = isThreadChatId(String(pipelineChatId))
|
|
889
|
+
? `_agent_${_targetKey}::${pipelineChatId}`
|
|
890
|
+
: `_agent_${_targetKey}`;
|
|
840
891
|
const member = _boundProj.team.find(t => t.key === _targetKey);
|
|
841
892
|
const label = member ? `${member.icon || '🤖'} ${member.name}` : _targetKey;
|
|
842
893
|
pipeline.clearQueue(vid);
|
|
843
894
|
const stopped = pipeline.interruptActive(vid);
|
|
844
895
|
if (stopped) {
|
|
845
|
-
await bot.sendMessage(
|
|
896
|
+
await bot.sendMessage(pipelineChatId, `⏹ Stopping ${label}...`);
|
|
846
897
|
} else {
|
|
847
|
-
await bot.sendMessage(
|
|
898
|
+
await bot.sendMessage(pipelineChatId, `${label} 当前没有活跃任务`);
|
|
848
899
|
}
|
|
849
900
|
return;
|
|
850
901
|
}
|
|
851
902
|
// /stop <bad-nickname> → no match, report error instead of falling through
|
|
852
903
|
if (_stopArg) {
|
|
853
|
-
await bot.sendMessage(
|
|
904
|
+
await bot.sendMessage(pipelineChatId, `❌ 未找到团队成员: ${_stopArg}`);
|
|
854
905
|
return;
|
|
855
906
|
}
|
|
856
907
|
// Bare /stop, no sticky set → fall through to handleCommand
|
|
@@ -861,13 +912,13 @@ function createBridgeStarter(deps) {
|
|
|
861
912
|
// a) agentKey = known team member → route to that member (set sticky)
|
|
862
913
|
// b) agentKey = null, mapping found → user replied to main; clear sticky, route to main
|
|
863
914
|
// c) parentId present, no mapping → intent is explicit, avoid sticky; clear sticky, route to main
|
|
864
|
-
if (
|
|
915
|
+
if (_isQuotedReply) {
|
|
865
916
|
if (_replyAgentKey) {
|
|
866
917
|
const member = _boundProj.team.find(m => m.key === _replyAgentKey);
|
|
867
918
|
if (member) {
|
|
868
919
|
_setSticky(member.key);
|
|
869
920
|
log('INFO', `Quoted reply → force route to ${_replyAgentKey} (sticky set)`);
|
|
870
|
-
_dispatchToTeamMember(member, _boundProj, trimmedText, liveCfg, bot,
|
|
921
|
+
_dispatchToTeamMember(member, _boundProj, trimmedText, liveCfg, bot, pipelineChatId, executeTaskByName, acl);
|
|
871
922
|
return;
|
|
872
923
|
}
|
|
873
924
|
// agentKey set but not a current team member → fall through to main
|
|
@@ -876,7 +927,7 @@ function createBridgeStarter(deps) {
|
|
|
876
927
|
// Cases b & c: no agentKey (main agent) or stale/unknown agentKey
|
|
877
928
|
_clearSticky();
|
|
878
929
|
log('INFO', `Quoted reply → route to main (agentKey=${_replyAgentKey} mappingFound=${_replyMappingFound})`);
|
|
879
|
-
await pipeline.processMessage(
|
|
930
|
+
await pipeline.processMessage(pipelineChatId, trimmedText, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
|
|
880
931
|
return;
|
|
881
932
|
}
|
|
882
933
|
// 1. Explicit nickname → route + set sticky
|
|
@@ -887,10 +938,13 @@ function createBridgeStarter(deps) {
|
|
|
887
938
|
if (!rest) {
|
|
888
939
|
// Pure nickname, no task — confirm member is online
|
|
889
940
|
log('INFO', `Sticky set (pure nickname): ${_chatKey.slice(-8)} → ${member.key}`);
|
|
890
|
-
bot.sendMarkdown(
|
|
941
|
+
bot.sendMarkdown(pipelineChatId, `${member.icon || '🤖'} **${member.name}** 在线`)
|
|
891
942
|
.then((msg) => {
|
|
892
943
|
if (msg && msg.message_id) {
|
|
893
|
-
|
|
944
|
+
const _vidNick = isThreadChatId(String(pipelineChatId))
|
|
945
|
+
? `_agent_${member.key}::${pipelineChatId}`
|
|
946
|
+
: `_agent_${member.key}`;
|
|
947
|
+
trackBridgeReplyMapping(msg.message_id, inferSessionMapping(_vidNick, {
|
|
894
948
|
agentKey: member.key,
|
|
895
949
|
cwd: member.cwd || _boundProj.cwd,
|
|
896
950
|
engine: member.engine || _boundProj.engine || 'claude',
|
|
@@ -901,7 +955,7 @@ function createBridgeStarter(deps) {
|
|
|
901
955
|
return;
|
|
902
956
|
}
|
|
903
957
|
log('INFO', `Sticky set: ${_chatKey.slice(-8)} → ${member.key}`);
|
|
904
|
-
_dispatchToTeamMember(member, _boundProj, rest, liveCfg, bot,
|
|
958
|
+
_dispatchToTeamMember(member, _boundProj, rest, liveCfg, bot, pipelineChatId, executeTaskByName, acl);
|
|
905
959
|
return;
|
|
906
960
|
}
|
|
907
961
|
|
|
@@ -914,7 +968,7 @@ function createBridgeStarter(deps) {
|
|
|
914
968
|
const rest = trimmedText.slice(_mainMatch.length).replace(/^[\s,,::]+/, '');
|
|
915
969
|
log('INFO', `Main nickname → cleared sticky, routing to main${rest ? ` (task: ${rest.slice(0, 30)})` : ''}`);
|
|
916
970
|
if (!rest) {
|
|
917
|
-
bot.sendMarkdown(
|
|
971
|
+
bot.sendMarkdown(pipelineChatId, `${_boundProj.icon || '🤖'} **${_boundProj.name}** 在线`)
|
|
918
972
|
.then((msg) => {
|
|
919
973
|
if (msg && msg.message_id) {
|
|
920
974
|
trackBridgeReplyMapping(msg.message_id, inferSessionMapping(String(chatId), {
|
|
@@ -929,10 +983,10 @@ function createBridgeStarter(deps) {
|
|
|
929
983
|
return;
|
|
930
984
|
}
|
|
931
985
|
try {
|
|
932
|
-
await pipeline.processMessage(
|
|
986
|
+
await pipeline.processMessage(pipelineChatId, rest, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
|
|
933
987
|
} catch (e) {
|
|
934
988
|
log('ERROR', `Team main-route handleCommand failed: ${e.message}`);
|
|
935
|
-
bot.sendMessage(
|
|
989
|
+
bot.sendMessage(pipelineChatId, `❌ 执行失败: ${e.message}`).catch(() => {});
|
|
936
990
|
}
|
|
937
991
|
return;
|
|
938
992
|
}
|
|
@@ -942,7 +996,7 @@ function createBridgeStarter(deps) {
|
|
|
942
996
|
const member = _boundProj.team.find(m => m.key === _stickyKey);
|
|
943
997
|
if (member) {
|
|
944
998
|
log('INFO', `Sticky route: → ${_stickyKey}`);
|
|
945
|
-
_dispatchToTeamMember(member, _boundProj, trimmedText, liveCfg, bot,
|
|
999
|
+
_dispatchToTeamMember(member, _boundProj, trimmedText, liveCfg, bot, pipelineChatId, executeTaskByName, acl);
|
|
946
1000
|
return;
|
|
947
1001
|
}
|
|
948
1002
|
}
|
|
@@ -950,10 +1004,10 @@ function createBridgeStarter(deps) {
|
|
|
950
1004
|
}
|
|
951
1005
|
|
|
952
1006
|
try {
|
|
953
|
-
await pipeline.processMessage(
|
|
1007
|
+
await pipeline.processMessage(pipelineChatId, text, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
|
|
954
1008
|
} catch (e) {
|
|
955
1009
|
log('ERROR', `Feishu handleCommand failed for ${chatId}: ${e.message}`);
|
|
956
|
-
bot.sendMessage(
|
|
1010
|
+
bot.sendMessage(pipelineChatId, `❌ 命令执行失败: ${e.message}`).catch(() => {});
|
|
957
1011
|
}
|
|
958
1012
|
}
|
|
959
1013
|
}, { log: (lvl, msg) => log(lvl, msg) });
|