metame-cli 1.5.24 → 1.5.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metame-cli",
3
- "version": "1.5.24",
3
+ "version": "1.5.26",
4
4
  "description": "The Cognitive Profile Layer for Claude Code. Knows how you think, not just what you said.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -224,6 +224,21 @@ function createBridgeStarter(deps) {
224
224
  },
225
225
  });
226
226
  }
227
+
228
+ function _createPipelineTarget({ pipelineChatId, effectiveChatId, bot }) {
229
+ const replyChatId = String(pipelineChatId || '').trim();
230
+ const processChatId = String(effectiveChatId || pipelineChatId || '').trim();
231
+ if (!replyChatId || !processChatId) {
232
+ return { processChatId: replyChatId || processChatId, bot };
233
+ }
234
+ if (replyChatId === processChatId) {
235
+ return { processChatId, bot };
236
+ }
237
+ return {
238
+ processChatId,
239
+ bot: _createTeamProxyBot(bot, replyChatId),
240
+ };
241
+ }
227
242
  // Get team member's working directory inside the source tree, never under ~/.metame.
228
243
  // Creates agents/<key>/ directory by default, or ensures an explicit member.cwd exists.
229
244
  function _getMemberCwd(parentCwd, key, explicitCwd = null) {
@@ -877,6 +892,13 @@ function createBridgeStarter(deps) {
877
892
  // Use pipelineChatId so each topic gets independent sticky state
878
893
  const _chatKey = String(pipelineChatId);
879
894
  const _rawChatKey = _threadRawChatId(_chatKey);
895
+ const _topicMainRoute = !!(
896
+ threadRootId
897
+ && _parentMapping
898
+ && !_isQuotedReply
899
+ && _parentMapping.logicalChatId
900
+ && !String(_parentMapping.logicalChatId).startsWith('_agent_')
901
+ );
880
902
  const _setSticky = (key) => {
881
903
  if (!_st.team_sticky) _st.team_sticky = {};
882
904
  _st.team_sticky[_chatKey] = key;
@@ -899,6 +921,11 @@ function createBridgeStarter(deps) {
899
921
  saveState(_st);
900
922
  };
901
923
  let _stickyKey = (_st.team_sticky || {})[_chatKey] || (_st.team_sticky || {})[_rawChatKey] || null;
924
+ const _pipelineTarget = _createPipelineTarget({
925
+ pipelineChatId,
926
+ effectiveChatId: _topicMainRoute ? chatId : pipelineChatId,
927
+ bot,
928
+ });
902
929
 
903
930
  // Team group routing: if bound project has a team array, check message for member nickname
904
931
  // Non-/stop slash commands bypass team routing → handled by main project
@@ -978,7 +1005,13 @@ function createBridgeStarter(deps) {
978
1005
  // Cases b & c: no agentKey (main agent) or stale/unknown agentKey
979
1006
  _clearSticky();
980
1007
  log('INFO', `Quoted reply → route to main (agentKey=${_replyAgentKey} mappingFound=${_replyMappingFound})`);
981
- await pipeline.processMessage(pipelineChatId, trimmedText, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
1008
+ await pipeline.processMessage(_pipelineTarget.processChatId, trimmedText, {
1009
+ bot: _pipelineTarget.bot,
1010
+ config: liveCfg,
1011
+ executeTaskByName,
1012
+ senderId: acl.senderId,
1013
+ readOnly: acl.readOnly,
1014
+ });
982
1015
  return;
983
1016
  }
984
1017
  // 1. Explicit nickname → route + set sticky
@@ -1034,7 +1067,13 @@ function createBridgeStarter(deps) {
1034
1067
  return;
1035
1068
  }
1036
1069
  try {
1037
- await pipeline.processMessage(pipelineChatId, rest, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
1070
+ await pipeline.processMessage(_pipelineTarget.processChatId, rest, {
1071
+ bot: _pipelineTarget.bot,
1072
+ config: liveCfg,
1073
+ executeTaskByName,
1074
+ senderId: acl.senderId,
1075
+ readOnly: acl.readOnly,
1076
+ });
1038
1077
  } catch (e) {
1039
1078
  log('ERROR', `Team main-route handleCommand failed: ${e.message}`);
1040
1079
  bot.sendMessage(pipelineChatId, `❌ 执行失败: ${e.message}`).catch(() => {});
@@ -1058,7 +1097,13 @@ function createBridgeStarter(deps) {
1058
1097
  }
1059
1098
 
1060
1099
  try {
1061
- await pipeline.processMessage(pipelineChatId, text, { bot, config: liveCfg, executeTaskByName, senderId: acl.senderId, readOnly: acl.readOnly });
1100
+ await pipeline.processMessage(_pipelineTarget.processChatId, text, {
1101
+ bot: _pipelineTarget.bot,
1102
+ config: liveCfg,
1103
+ executeTaskByName,
1104
+ senderId: acl.senderId,
1105
+ readOnly: acl.readOnly,
1106
+ });
1062
1107
  } catch (e) {
1063
1108
  log('ERROR', `Feishu handleCommand failed for ${chatId}: ${e.message}`);
1064
1109
  bot.sendMessage(pipelineChatId, `❌ 命令执行失败: ${e.message}`).catch(() => {});
@@ -431,7 +431,8 @@ function createCommandRouter(deps) {
431
431
  !!curHasEngine &&
432
432
  cur.cwd !== projCwd &&
433
433
  !rawHasEngine;
434
- if (!cur || !curHasEngine || shouldReattachForCwdChange) {
434
+ const _isControlCmd = text && /^\/(stop|quit)$/.test(text.trim());
435
+ if (!_isControlCmd && (!cur || !curHasEngine || shouldReattachForCwdChange)) {
435
436
  const initReason = !cur ? 'no-session' : (!curHasEngine ? 'engine-missing' : 'cwd-changed');
436
437
  log('INFO', `SESSION-INIT [${String(sessionChatId).slice(-32)}] ${initReason}`);
437
438
  attachOrCreateSession(sessionChatId, projCwd, proj.name || mappedKey, targetEngine);
@@ -4,6 +4,7 @@ const { classifyTaskUsage } = require('./usage-classifier');
4
4
  const { normalizeModel } = require('./daemon-task-scheduler');
5
5
  const { resolveEngineModel } = require('./daemon-engine-runtime');
6
6
  const { createCommandSessionResolver } = require('./daemon-command-session-route');
7
+ const { isThreadChatId: _isThreadChatId, rawChatId: _rawThreadChatId } = require('./core/thread-chat-id');
7
8
 
8
9
  function createExecCommandHandler(deps) {
9
10
  const {
@@ -218,7 +219,12 @@ function createExecCommandHandler(deps) {
218
219
  const _pl = pipeline && pipeline.current;
219
220
  if (_pl) {
220
221
  _pl.clearQueue(chatId);
221
- const stopped = _pl.interruptActive(chatId);
222
+ let stopped = _pl.interruptActive(chatId);
223
+ if (!stopped && _isThreadChatId(chatId)) {
224
+ // Thread-scoped /stop: fall back to raw chatId (task may be keyed at group level).
225
+ // Do NOT clearQueue(_raw) — that would discard queued tasks from other threads.
226
+ stopped = _pl.interruptActive(_rawThreadChatId(chatId));
227
+ }
222
228
  if (stopped) {
223
229
  await bot.sendMessage(chatId, '⏹ Stopping current engine task...');
224
230
  } else {
@@ -226,7 +232,8 @@ function createExecCommandHandler(deps) {
226
232
  }
227
233
  } else {
228
234
  // Fallback: direct activeProcesses manipulation (pipeline not yet initialized)
229
- const proc = activeProcesses.get(chatId);
235
+ const _raw = _isThreadChatId(chatId) ? _rawThreadChatId(chatId) : null;
236
+ const proc = activeProcesses.get(chatId) || (_raw && activeProcesses.get(_raw));
230
237
  if (proc && proc.child) {
231
238
  proc.aborted = true;
232
239
  const signal = proc.killSignal || 'SIGTERM';
@@ -248,9 +255,12 @@ function createExecCommandHandler(deps) {
248
255
  const _pl = pipeline && pipeline.current;
249
256
  if (_pl) {
250
257
  _pl.clearQueue(chatId);
251
- _pl.interruptActive(chatId);
258
+ if (!_pl.interruptActive(chatId) && _isThreadChatId(chatId)) {
259
+ _pl.interruptActive(_rawThreadChatId(chatId));
260
+ }
252
261
  } else {
253
- const proc = activeProcesses.get(chatId);
262
+ const _raw = _isThreadChatId(chatId) ? _rawThreadChatId(chatId) : null;
263
+ const proc = activeProcesses.get(chatId) || (_raw && activeProcesses.get(_raw));
254
264
  if (proc && proc.child) {
255
265
  proc.aborted = true;
256
266
  const signal = proc.killSignal || 'SIGTERM';