metame-cli 1.5.4 → 1.5.6
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.md +6 -1
- package/index.js +277 -55
- package/package.json +3 -2
- package/scripts/agent-layer.js +4 -2
- package/scripts/bin/dispatch_to +18 -6
- package/scripts/bin/push-clean.sh +72 -0
- package/scripts/daemon-admin-commands.js +266 -64
- package/scripts/daemon-agent-commands.js +188 -66
- package/scripts/daemon-bridges.js +475 -50
- package/scripts/daemon-checkpoints.js +84 -30
- package/scripts/daemon-claude-engine.js +651 -103
- package/scripts/daemon-command-router.js +134 -27
- package/scripts/daemon-command-session-route.js +118 -0
- package/scripts/daemon-default.yaml +2 -0
- package/scripts/daemon-dispatch-cards.js +185 -0
- package/scripts/daemon-engine-runtime.js +96 -20
- package/scripts/daemon-exec-commands.js +106 -50
- package/scripts/daemon-file-browser.js +63 -7
- package/scripts/daemon-notify.js +18 -4
- package/scripts/daemon-ops-commands.js +28 -6
- package/scripts/daemon-remote-dispatch.js +34 -2
- package/scripts/daemon-session-commands.js +102 -45
- package/scripts/daemon-session-store.js +497 -66
- package/scripts/daemon-siri-bridge.js +234 -0
- package/scripts/daemon-siri-imessage.js +209 -0
- package/scripts/daemon-task-scheduler.js +10 -2
- package/scripts/{team-dispatch.js → daemon-team-dispatch.js} +150 -11
- package/scripts/daemon.js +484 -181
- package/scripts/docs/hook-config.md +7 -4
- package/scripts/docs/maintenance-manual.md +10 -3
- package/scripts/docs/pointer-map.md +2 -2
- package/scripts/feishu-adapter.js +7 -15
- package/scripts/hooks/doc-router.js +29 -0
- package/scripts/hooks/intent-doc-router.js +54 -0
- package/scripts/hooks/intent-engine.js +9 -40
- package/scripts/intent-registry.js +59 -0
- package/scripts/memory-extract.js +59 -0
- package/scripts/mentor-engine.js +6 -0
- package/scripts/schema.js +1 -0
- package/scripts/self-reflect.js +110 -12
- package/scripts/session-analytics.js +160 -0
- package/scripts/signal-capture.js +1 -1
- package/scripts/hooks/intent-agent-manage.js +0 -50
- package/scripts/hooks/intent-hook-config.js +0 -28
|
@@ -7,8 +7,14 @@ const {
|
|
|
7
7
|
} = require('./usage-classifier');
|
|
8
8
|
const { IS_WIN } = require('./platform');
|
|
9
9
|
const { ENGINE_MODEL_CONFIG, resolveEngineModel } = require('./daemon-engine-runtime');
|
|
10
|
-
const { resolveProjectKey: _resolveProjectKey } = require('./team-dispatch');
|
|
11
|
-
const {
|
|
10
|
+
const { resolveProjectKey: _resolveProjectKey } = require('./daemon-team-dispatch');
|
|
11
|
+
const {
|
|
12
|
+
parseRemoteTargetRef,
|
|
13
|
+
getRemoteDispatchStatus,
|
|
14
|
+
generatePairCode,
|
|
15
|
+
isValidPairCode,
|
|
16
|
+
deriveSecretFromPairCode,
|
|
17
|
+
} = require('./daemon-remote-dispatch');
|
|
12
18
|
let mentorEngine = null;
|
|
13
19
|
try { mentorEngine = require('./mentor-engine'); } catch { /* optional */ }
|
|
14
20
|
|
|
@@ -41,7 +47,7 @@ function createAdminCommandHandler(deps) {
|
|
|
41
47
|
getDistillModel = () => 'haiku',
|
|
42
48
|
} = deps;
|
|
43
49
|
|
|
44
|
-
// resolveProjectKey: imported from team-dispatch.js (shared with dispatch_to and daemon.js)
|
|
50
|
+
// resolveProjectKey: imported from daemon-team-dispatch.js (shared with dispatch_to and daemon.js)
|
|
45
51
|
const resolveProjectKey = _resolveProjectKey;
|
|
46
52
|
|
|
47
53
|
/**
|
|
@@ -71,6 +77,14 @@ function createAdminCommandHandler(deps) {
|
|
|
71
77
|
return map[String(chatId)] || 'user';
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
function resolveBoundProjectKey(chatId, config) {
|
|
81
|
+
const map = {
|
|
82
|
+
...(config && config.feishu ? config.feishu.chat_agent_map : {}),
|
|
83
|
+
...(config && config.telegram ? config.telegram.chat_agent_map : {}),
|
|
84
|
+
};
|
|
85
|
+
return map[String(chatId)] || '';
|
|
86
|
+
}
|
|
87
|
+
|
|
74
88
|
function popFlag(input, flagName) {
|
|
75
89
|
const src = String(input || '');
|
|
76
90
|
const re = new RegExp(`(?:^|\\s)--${flagName}\\s+(\\S+)`, 'i');
|
|
@@ -99,6 +113,87 @@ function createAdminCommandHandler(deps) {
|
|
|
99
113
|
};
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
function isLikelyTeamTaskResumeIntent(text) {
|
|
117
|
+
const src = String(text || '').trim();
|
|
118
|
+
if (!src || src.startsWith('/')) return false;
|
|
119
|
+
if (src.length < 4 || src.length > 120) return false;
|
|
120
|
+
if (/(?:新建|创建|查看|列出|列表|详情|状态|有哪些|teamtask\s*$|\/teamtask)/i.test(src)) return false;
|
|
121
|
+
return /(?:继续(?:做|改|修)?|接着(?:做|改|修)?|续上|接续|返工|复工|再修(?:一下)?|再改(?:一下)?).*(?:上次|那个|这单|这个任务|任务|TeamTask|team task|工单)?|(?:上次|那个|这单|这个任务).*(?:继续|接着|返工|复工|再修|再改)/i.test(src);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function listAutoResumeCandidates(chatId, senderKey, config) {
|
|
125
|
+
if (!taskBoard || typeof taskBoard.listRecentTasks !== 'function') return [];
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
const chatKey = String(chatId);
|
|
128
|
+
const recent = taskBoard.listRecentTasks(12, null, 'team');
|
|
129
|
+
return recent.filter((task) => {
|
|
130
|
+
if (!task || task.task_kind !== 'team') return false;
|
|
131
|
+
if (!config.projects || !config.projects[task.to_agent]) return false;
|
|
132
|
+
const sourceChatId = String(task.inputs && task.inputs.source_chat_id || '').trim();
|
|
133
|
+
if (!sourceChatId || sourceChatId !== chatKey) return false;
|
|
134
|
+
const updatedAt = Date.parse(task.updated_at || task.created_at || '');
|
|
135
|
+
if (!Number.isFinite(updatedAt) || (now - updatedAt) > 12 * 3600_000) return false;
|
|
136
|
+
const participants = Array.isArray(task.participants) ? task.participants : [];
|
|
137
|
+
return task.from_agent === senderKey || participants.includes(senderKey);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function buildTeamTaskResumeEnvelope(task, targetKey, chatId, config) {
|
|
142
|
+
return taskEnvelope && taskEnvelope.normalizeTaskEnvelope
|
|
143
|
+
? taskEnvelope.normalizeTaskEnvelope({
|
|
144
|
+
...task,
|
|
145
|
+
status: 'queued',
|
|
146
|
+
updated_at: new Date().toISOString(),
|
|
147
|
+
task_kind: 'team',
|
|
148
|
+
participants: taskBoard.listScopeParticipants(task.scope_id || task.task_id),
|
|
149
|
+
}, {
|
|
150
|
+
from_agent: task.from_agent || resolveSenderKey(chatId, config),
|
|
151
|
+
to_agent: targetKey,
|
|
152
|
+
scope_id: task.scope_id || task.task_id,
|
|
153
|
+
})
|
|
154
|
+
: {
|
|
155
|
+
task_id: task.task_id,
|
|
156
|
+
scope_id: task.scope_id || task.task_id,
|
|
157
|
+
from_agent: task.from_agent || resolveSenderKey(chatId, config),
|
|
158
|
+
to_agent: targetKey,
|
|
159
|
+
participants: taskBoard.listScopeParticipants(task.scope_id || task.task_id),
|
|
160
|
+
goal: task.goal,
|
|
161
|
+
definition_of_done: task.definition_of_done || [],
|
|
162
|
+
inputs: task.inputs || {},
|
|
163
|
+
artifacts: task.artifacts || [],
|
|
164
|
+
owned_paths: task.owned_paths || [],
|
|
165
|
+
priority: task.priority || 'normal',
|
|
166
|
+
status: 'queued',
|
|
167
|
+
task_kind: 'team',
|
|
168
|
+
created_at: task.created_at,
|
|
169
|
+
updated_at: new Date().toISOString(),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function dispatchTeamTaskResume(task, chatId, config, senderId = null) {
|
|
174
|
+
const targetKey = task.to_agent;
|
|
175
|
+
if (!config.projects || !config.projects[targetKey]) {
|
|
176
|
+
return { success: false, error: `target_missing:${targetKey}` };
|
|
177
|
+
}
|
|
178
|
+
const envelope = buildTeamTaskResumeEnvelope(task, targetKey, chatId, config);
|
|
179
|
+
const result = dispatchTask(targetKey, {
|
|
180
|
+
from: envelope.from_agent || 'user',
|
|
181
|
+
type: 'task',
|
|
182
|
+
priority: envelope.priority || 'normal',
|
|
183
|
+
payload: {
|
|
184
|
+
title: envelope.goal.slice(0, 60),
|
|
185
|
+
prompt: envelope.goal,
|
|
186
|
+
task_envelope: envelope,
|
|
187
|
+
},
|
|
188
|
+
callback: false,
|
|
189
|
+
new_session: false,
|
|
190
|
+
source_chat_id: String(chatId),
|
|
191
|
+
source_sender_key: envelope.from_agent || resolveSenderKey(chatId, config),
|
|
192
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
193
|
+
}, config);
|
|
194
|
+
return { success: !!(result && result.success), result, envelope, targetKey };
|
|
195
|
+
}
|
|
196
|
+
|
|
102
197
|
function formatTaskSchedule(task) {
|
|
103
198
|
const at = typeof task.at === 'string' ? task.at.trim() : '';
|
|
104
199
|
if (at) {
|
|
@@ -167,8 +262,30 @@ function createAdminCommandHandler(deps) {
|
|
|
167
262
|
}
|
|
168
263
|
}
|
|
169
264
|
|
|
265
|
+
async function sendLocalDispatchReceipt(bot, chatId, targetKey, projInfo, result, preview) {
|
|
266
|
+
if (!result || !result.success) return;
|
|
267
|
+
const icon = projInfo && projInfo.icon ? projInfo.icon : '🤖';
|
|
268
|
+
const name = projInfo && projInfo.name ? projInfo.name : targetKey;
|
|
269
|
+
const lines = [
|
|
270
|
+
'📮 Dispatch 回执',
|
|
271
|
+
'',
|
|
272
|
+
`状态: ${icon} ${name} 已接收并入队`,
|
|
273
|
+
];
|
|
274
|
+
if (result.id) lines.push(`编号: ${result.id}`);
|
|
275
|
+
if (preview) lines.push(`摘要: ${String(preview).slice(0, 120)}`);
|
|
276
|
+
if (result.task_id) {
|
|
277
|
+
lines.push('');
|
|
278
|
+
lines.push(`TeamTask: ${result.task_id}`);
|
|
279
|
+
if (result.scope_id && result.scope_id !== result.task_id) {
|
|
280
|
+
lines.push(`Scope: ${result.scope_id}`);
|
|
281
|
+
}
|
|
282
|
+
lines.push(`如需复工,请使用: /TeamTask resume ${result.task_id}`);
|
|
283
|
+
}
|
|
284
|
+
await bot.sendMessage(chatId, lines.join('\n'));
|
|
285
|
+
}
|
|
286
|
+
|
|
170
287
|
async function handleAdminCommand(ctx) {
|
|
171
|
-
const { bot, chatId, text } = ctx;
|
|
288
|
+
const { bot, chatId, text, senderId = null } = ctx;
|
|
172
289
|
const state = ctx.state || {};
|
|
173
290
|
let config = ctx.config || {};
|
|
174
291
|
|
|
@@ -368,6 +485,39 @@ function createAdminCommandHandler(deps) {
|
|
|
368
485
|
return { handled: true, config };
|
|
369
486
|
}
|
|
370
487
|
|
|
488
|
+
if (isLikelyTeamTaskResumeIntent(text)) {
|
|
489
|
+
const senderKey = resolveSenderKey(chatId, config);
|
|
490
|
+
const candidates = listAutoResumeCandidates(chatId, senderKey, config);
|
|
491
|
+
if (candidates.length === 1) {
|
|
492
|
+
const task = candidates[0];
|
|
493
|
+
const resumed = dispatchTeamTaskResume(task, chatId, config, senderId);
|
|
494
|
+
if (resumed.success) {
|
|
495
|
+
if (taskBoard && typeof taskBoard.appendTaskEvent === 'function') {
|
|
496
|
+
taskBoard.appendTaskEvent(task.task_id, 'task_resume_requested', String(chatId), { by: String(chatId), source: 'nl_auto_resume' });
|
|
497
|
+
}
|
|
498
|
+
await bot.sendMessage(chatId, [
|
|
499
|
+
`🔄 已自动续跑最近的 TeamTask: ${task.task_id}`,
|
|
500
|
+
`目标: ${task.to_agent}`,
|
|
501
|
+
`意图: ${text.trim().slice(0, 80)}`,
|
|
502
|
+
'回执会在目标端真正接收后返回。',
|
|
503
|
+
].join('\n'));
|
|
504
|
+
await sendLocalDispatchReceipt(bot, chatId, resumed.targetKey, config.projects[resumed.targetKey], resumed.result, resumed.envelope.goal);
|
|
505
|
+
return { handled: true, config };
|
|
506
|
+
}
|
|
507
|
+
await bot.sendMessage(chatId, `❌ 自动续跑失败: ${resumed.result && resumed.result.error ? resumed.result.error : 'unknown_error'}`);
|
|
508
|
+
return { handled: true, config };
|
|
509
|
+
}
|
|
510
|
+
if (candidates.length > 1) {
|
|
511
|
+
const lines = ['⚠️ 检测到你可能想复工 TeamTask,但最近有多条候选任务:'];
|
|
512
|
+
for (const task of candidates.slice(0, 3)) {
|
|
513
|
+
lines.push(`- ${task.task_id} [${task.status}] ${task.goal.slice(0, 50)}`);
|
|
514
|
+
}
|
|
515
|
+
lines.push('请直接回复更明确一点,或使用 /TeamTask 查看后再选择。');
|
|
516
|
+
await bot.sendMessage(chatId, lines.join('\n'));
|
|
517
|
+
return { handled: true, config };
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
371
521
|
// /TeamTask — create/list/detail/resume team collaboration tasks
|
|
372
522
|
const teamTaskCmdMatch = text.match(/^\/teamtask(?:\s+([\s\S]+))?$/i);
|
|
373
523
|
if (teamTaskCmdMatch) {
|
|
@@ -432,13 +582,18 @@ function createAdminCommandHandler(deps) {
|
|
|
432
582
|
task_envelope: envelope,
|
|
433
583
|
},
|
|
434
584
|
callback: false,
|
|
585
|
+
source_chat_id: String(chatId),
|
|
586
|
+
source_sender_key: senderKey,
|
|
587
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
435
588
|
}, config);
|
|
436
589
|
if (result.success) {
|
|
437
590
|
await bot.sendMessage(chatId, [
|
|
438
|
-
`✅ 已创建 TeamTask
|
|
591
|
+
`✅ 已创建 TeamTask 并提交派发: ${envelope.task_id}`,
|
|
439
592
|
`Scope: ${envelope.scope_id || envelope.task_id}`,
|
|
593
|
+
'回执会在目标端真正接收后返回。',
|
|
440
594
|
`查看: /TeamTask ${envelope.task_id}`,
|
|
441
595
|
].join('\n'));
|
|
596
|
+
await sendLocalDispatchReceipt(bot, chatId, targetKey, config.projects[targetKey], result, goal);
|
|
442
597
|
} else {
|
|
443
598
|
await bot.sendMessage(chatId, `❌ 创建 TeamTask 失败: ${result.error}`);
|
|
444
599
|
}
|
|
@@ -478,52 +633,13 @@ function createAdminCommandHandler(deps) {
|
|
|
478
633
|
await bot.sendMessage(chatId, `❌ 目标 agent 不存在: ${targetKey}`);
|
|
479
634
|
return { handled: true, config };
|
|
480
635
|
}
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
...task,
|
|
484
|
-
status: 'queued',
|
|
485
|
-
updated_at: new Date().toISOString(),
|
|
486
|
-
task_kind: 'team',
|
|
487
|
-
participants: taskBoard.listScopeParticipants(task.scope_id || task.task_id),
|
|
488
|
-
}, {
|
|
489
|
-
from_agent: task.from_agent || resolveSenderKey(chatId, config),
|
|
490
|
-
to_agent: targetKey,
|
|
491
|
-
scope_id: task.scope_id || task.task_id,
|
|
492
|
-
})
|
|
493
|
-
: {
|
|
494
|
-
task_id: task.task_id,
|
|
495
|
-
scope_id: task.scope_id || task.task_id,
|
|
496
|
-
from_agent: task.from_agent || resolveSenderKey(chatId, config),
|
|
497
|
-
to_agent: targetKey,
|
|
498
|
-
participants: taskBoard.listScopeParticipants(task.scope_id || task.task_id),
|
|
499
|
-
goal: task.goal,
|
|
500
|
-
definition_of_done: task.definition_of_done || [],
|
|
501
|
-
inputs: task.inputs || {},
|
|
502
|
-
artifacts: task.artifacts || [],
|
|
503
|
-
owned_paths: task.owned_paths || [],
|
|
504
|
-
priority: task.priority || 'normal',
|
|
505
|
-
status: 'queued',
|
|
506
|
-
task_kind: 'team',
|
|
507
|
-
created_at: task.created_at,
|
|
508
|
-
updated_at: new Date().toISOString(),
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
const result = dispatchTask(targetKey, {
|
|
512
|
-
from: envelope.from_agent || 'user',
|
|
513
|
-
type: 'task',
|
|
514
|
-
priority: envelope.priority || 'normal',
|
|
515
|
-
payload: {
|
|
516
|
-
title: envelope.goal.slice(0, 60),
|
|
517
|
-
prompt: envelope.goal,
|
|
518
|
-
task_envelope: envelope,
|
|
519
|
-
},
|
|
520
|
-
callback: false,
|
|
521
|
-
new_session: false,
|
|
522
|
-
}, config);
|
|
636
|
+
const resumed = dispatchTeamTaskResume(task, chatId, config, senderId);
|
|
637
|
+
const { result, envelope } = resumed;
|
|
523
638
|
|
|
524
639
|
if (result.success) {
|
|
525
640
|
taskBoard.appendTaskEvent(task.task_id, 'task_resume_requested', String(chatId), { by: String(chatId) });
|
|
526
|
-
await bot.sendMessage(chatId, `✅ 已续跑 TeamTask: ${task.task_id}
|
|
641
|
+
await bot.sendMessage(chatId, `✅ 已续跑 TeamTask: ${task.task_id}\n回执会在目标端真正接收后返回。`);
|
|
642
|
+
await sendLocalDispatchReceipt(bot, chatId, targetKey, config.projects[targetKey], result, envelope.goal);
|
|
527
643
|
} else {
|
|
528
644
|
await bot.sendMessage(chatId, `❌ 续跑失败: ${result.error}`);
|
|
529
645
|
}
|
|
@@ -626,12 +742,12 @@ function createAdminCommandHandler(deps) {
|
|
|
626
742
|
|
|
627
743
|
// /dispatch peers — show remote dispatch config
|
|
628
744
|
if (args === 'peers') {
|
|
629
|
-
const rd =
|
|
745
|
+
const rd = getRemoteDispatchStatus(config);
|
|
630
746
|
if (!rd) {
|
|
631
747
|
await bot.sendMessage(chatId, '📡 远端 Dispatch 未配置\n\n在 daemon.yaml 中设置 feishu.remote_dispatch 启用。');
|
|
632
748
|
return { handled: true, config };
|
|
633
749
|
}
|
|
634
|
-
let msg = `📡 远端 Dispatch 配置\n─────────────\nself: ${rd.selfPeer}\nrelay chat: ${rd.chatId}\n\n远端成员:\n`;
|
|
750
|
+
let msg = `📡 远端 Dispatch 配置\n─────────────\nself: ${rd.selfPeer}\nrelay chat: ${rd.chatId}\nmode: pair code\nsecret: ${rd.hasSecret ? 'configured' : 'missing'}\n\n远端成员:\n`;
|
|
635
751
|
let hasRemote = false;
|
|
636
752
|
for (const [key, proj] of Object.entries(config.projects || {})) {
|
|
637
753
|
if (!Array.isArray(proj.team)) continue;
|
|
@@ -647,6 +763,49 @@ function createAdminCommandHandler(deps) {
|
|
|
647
763
|
return { handled: true, config };
|
|
648
764
|
}
|
|
649
765
|
|
|
766
|
+
if (args === 'code') {
|
|
767
|
+
const rd = getRemoteDispatchStatus(config);
|
|
768
|
+
if (!rd) {
|
|
769
|
+
await bot.sendMessage(chatId, '📡 远端 Dispatch 未配置\n\n在 daemon.yaml 中设置 feishu.remote_dispatch 启用。');
|
|
770
|
+
return { handled: true, config };
|
|
771
|
+
}
|
|
772
|
+
const code = generatePairCode();
|
|
773
|
+
const secret = deriveSecretFromPairCode(code, rd.chatId);
|
|
774
|
+
backupConfig();
|
|
775
|
+
const cfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
776
|
+
if (!cfg.feishu) cfg.feishu = {};
|
|
777
|
+
if (!cfg.feishu.remote_dispatch) cfg.feishu.remote_dispatch = {};
|
|
778
|
+
cfg.feishu.remote_dispatch.secret = secret;
|
|
779
|
+
writeConfigSafe(cfg);
|
|
780
|
+
config = loadConfig();
|
|
781
|
+
await bot.sendMessage(chatId, `🔐 配对码已生成\n\n配对码: ${code}\n\n把这 6 位码发到另一台设备执行:\n/dispatch pair ${code}`);
|
|
782
|
+
return { handled: true, config };
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const pairMatch = args.match(/^pair\s+(\d{6})$/);
|
|
786
|
+
if (pairMatch) {
|
|
787
|
+
const rd = getRemoteDispatchStatus(config);
|
|
788
|
+
if (!rd) {
|
|
789
|
+
await bot.sendMessage(chatId, '📡 远端 Dispatch 未配置\n\n在 daemon.yaml 中设置 feishu.remote_dispatch 启用。');
|
|
790
|
+
return { handled: true, config };
|
|
791
|
+
}
|
|
792
|
+
const code = pairMatch[1];
|
|
793
|
+
if (!isValidPairCode(code)) {
|
|
794
|
+
await bot.sendMessage(chatId, '❌ 配对码必须是 6 位数字');
|
|
795
|
+
return { handled: true, config };
|
|
796
|
+
}
|
|
797
|
+
const secret = deriveSecretFromPairCode(code, rd.chatId);
|
|
798
|
+
backupConfig();
|
|
799
|
+
const cfg = yaml.load(fs.readFileSync(CONFIG_FILE, 'utf8')) || {};
|
|
800
|
+
if (!cfg.feishu) cfg.feishu = {};
|
|
801
|
+
if (!cfg.feishu.remote_dispatch) cfg.feishu.remote_dispatch = {};
|
|
802
|
+
cfg.feishu.remote_dispatch.secret = secret;
|
|
803
|
+
writeConfigSafe(cfg);
|
|
804
|
+
config = loadConfig();
|
|
805
|
+
await bot.sendMessage(chatId, `✅ 配对码已写入\n\n当前设备: ${rd.selfPeer}\nrelay chat: ${rd.chatId}\n现在可以测试 /dispatch to <peer:project> ...`);
|
|
806
|
+
return { handled: true, config };
|
|
807
|
+
}
|
|
808
|
+
|
|
650
809
|
// /dispatch to <agent> <prompt>
|
|
651
810
|
const toMatch = args.match(/^to\s+(\S+)\s+(.+)$/s);
|
|
652
811
|
if (toMatch) {
|
|
@@ -664,6 +823,7 @@ function createAdminCommandHandler(deps) {
|
|
|
664
823
|
prompt,
|
|
665
824
|
source_chat_id: String(chatId),
|
|
666
825
|
source_sender_key: senderKey,
|
|
826
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
667
827
|
}, config);
|
|
668
828
|
if (res.success) {
|
|
669
829
|
await bot.sendMessage(chatId, `📡 已发送给 ${remoteTarget.peer}:${remoteTarget.project}`);
|
|
@@ -690,6 +850,7 @@ function createAdminCommandHandler(deps) {
|
|
|
690
850
|
prompt,
|
|
691
851
|
source_chat_id: String(chatId),
|
|
692
852
|
source_sender_key: senderKey,
|
|
853
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
693
854
|
}, config);
|
|
694
855
|
if (res.success) {
|
|
695
856
|
await bot.sendMessage(chatId, `📡 已发送给 ${projInfo.icon || '🤖'} ${projInfo.name || targetKey} (${projInfo.peer})`);
|
|
@@ -719,10 +880,14 @@ function createAdminCommandHandler(deps) {
|
|
|
719
880
|
priority: 'normal',
|
|
720
881
|
payload: { title: prompt.slice(0, 60), prompt },
|
|
721
882
|
callback: false,
|
|
883
|
+
source_chat_id: String(chatId),
|
|
884
|
+
source_sender_key: senderKey,
|
|
885
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
722
886
|
}, config, replyFn, dispatchStreamOptions);
|
|
723
887
|
|
|
724
888
|
if (result.success) {
|
|
725
|
-
await bot.sendMessage(chatId, `✅
|
|
889
|
+
await bot.sendMessage(chatId, `✅ 已提交派发给 ${projInfo.name || targetName},等待回执…`);
|
|
890
|
+
await sendLocalDispatchReceipt(bot, chatId, targetKey, projInfo, result, prompt);
|
|
726
891
|
} else {
|
|
727
892
|
await bot.sendMessage(chatId, `❌ 派发失败: ${result.error}`);
|
|
728
893
|
}
|
|
@@ -734,6 +899,8 @@ function createAdminCommandHandler(deps) {
|
|
|
734
899
|
'/dispatch status — 查看状态',
|
|
735
900
|
'/dispatch log — 查看记录',
|
|
736
901
|
'/dispatch peers — 查看远端配置',
|
|
902
|
+
'/dispatch code — 生成 6 位配对码并写入本机',
|
|
903
|
+
'/dispatch pair <123456> — 输入 6 位配对码写入本机',
|
|
737
904
|
'/dispatch to <agent> <任务内容> — 直接跨 agent 派发',
|
|
738
905
|
'/dispatch to <peer:project> <任务内容> — 跨设备派发',
|
|
739
906
|
'/TeamTask create <agent> <目标> [--scope <id>] [--parent <id>] — 创建/续接 TeamTask',
|
|
@@ -769,10 +936,14 @@ function createAdminCommandHandler(deps) {
|
|
|
769
936
|
priority: 'normal',
|
|
770
937
|
payload: { title: 'team message', prompt: `[来自团队的消息]\n\n${message}` },
|
|
771
938
|
callback: false,
|
|
939
|
+
source_chat_id: String(chatId),
|
|
940
|
+
source_sender_key: senderKey,
|
|
941
|
+
source_sender_id: String(senderId || '').trim() || '',
|
|
772
942
|
}, config, null, null);
|
|
773
943
|
|
|
774
944
|
if (result.success) {
|
|
775
945
|
await bot.sendMessage(chatId, `📬 已发送消息给 ${toProj.icon || '🤖'} ${toProj.name || targetKey}`);
|
|
946
|
+
await sendLocalDispatchReceipt(bot, chatId, targetKey, toProj, result, message);
|
|
776
947
|
} else {
|
|
777
948
|
await bot.sendMessage(chatId, `❌ 发送失败: ${result.error}`);
|
|
778
949
|
}
|
|
@@ -963,7 +1134,7 @@ function createAdminCommandHandler(deps) {
|
|
|
963
1134
|
const arg = text.slice('/mentor'.length).trim();
|
|
964
1135
|
|
|
965
1136
|
if (!arg || arg === 'status') {
|
|
966
|
-
const status = mentorEngine && typeof mentorEngine.getRuntimeStatus === 'function'
|
|
1137
|
+
const status = mentorCfg.enabled && mentorEngine && typeof mentorEngine.getRuntimeStatus === 'function'
|
|
967
1138
|
? mentorEngine.getRuntimeStatus()
|
|
968
1139
|
: { debt_count: 0, cooldown_remaining_ms: 0 };
|
|
969
1140
|
const mode = String(mentorCfg.mode || modeFromLevel(mentorCfg.friction_level));
|
|
@@ -983,6 +1154,9 @@ function createAdminCommandHandler(deps) {
|
|
|
983
1154
|
|
|
984
1155
|
if (arg === 'on' || arg === 'off') {
|
|
985
1156
|
mentorCfg.enabled = arg === 'on';
|
|
1157
|
+
if (!mentorCfg.enabled && mentorEngine && typeof mentorEngine.clearRuntime === 'function') {
|
|
1158
|
+
mentorEngine.clearRuntime();
|
|
1159
|
+
}
|
|
986
1160
|
writeConfigSafe(cfg);
|
|
987
1161
|
config = loadConfig();
|
|
988
1162
|
await bot.sendMessage(chatId, mentorCfg.enabled
|
|
@@ -1285,21 +1459,30 @@ function createAdminCommandHandler(deps) {
|
|
|
1285
1459
|
// Switching engine auto-syncs: distill model + preferred provider (if available)
|
|
1286
1460
|
if (text === '/engine' || text.startsWith('/engine ')) {
|
|
1287
1461
|
const arg = text.slice('/engine'.length).trim().toLowerCase();
|
|
1462
|
+
const boundProjectKey = resolveBoundProjectKey(chatId, config);
|
|
1463
|
+
const boundProject = boundProjectKey && config && config.projects ? config.projects[boundProjectKey] : null;
|
|
1288
1464
|
if (!arg) {
|
|
1289
|
-
const cur = getDefaultEngine();
|
|
1290
|
-
const
|
|
1291
|
-
const
|
|
1465
|
+
const cur = boundProject && boundProject.engine ? String(boundProject.engine).trim().toLowerCase() : getDefaultEngine();
|
|
1466
|
+
const safeCur = cur === 'codex' ? 'codex' : 'claude';
|
|
1467
|
+
const curEngineCfg = ENGINE_MODEL_CONFIG[safeCur] || ENGINE_MODEL_CONFIG.claude;
|
|
1468
|
+
const activeProvider = (safeCur === 'claude' && providerMod)
|
|
1292
1469
|
? providerMod.getActiveName()
|
|
1293
1470
|
: curEngineCfg.provider;
|
|
1294
1471
|
const distill = getDistillModel();
|
|
1295
1472
|
const daemonCfg = config.daemon || {};
|
|
1296
|
-
const currentModel = resolveEngineModel(
|
|
1473
|
+
const currentModel = resolveEngineModel(safeCur, daemonCfg, boundProject && boundProject.model);
|
|
1474
|
+
const scopeLine = boundProjectKey
|
|
1475
|
+
? `📍 当前 chat 绑定 Agent: ${boundProjectKey}`
|
|
1476
|
+
: `📍 当前 chat 使用全局默认引擎`;
|
|
1297
1477
|
await bot.sendMessage(chatId, [
|
|
1298
|
-
`🔧 引擎: ${
|
|
1478
|
+
`🔧 引擎: ${safeCur} | Provider: ${activeProvider}`,
|
|
1299
1479
|
`🤖 会话模型: ${currentModel} | 后台轻量: ${distill}`,
|
|
1480
|
+
scopeLine,
|
|
1300
1481
|
'',
|
|
1301
1482
|
'用法: /engine claude 或 /engine codex',
|
|
1302
|
-
|
|
1483
|
+
boundProjectKey
|
|
1484
|
+
? '当前 chat 已绑定 Agent;切换时会同步更新该 Agent 的 engine/model'
|
|
1485
|
+
: '切换引擎将自动同步 distill model 和首选 provider',
|
|
1303
1486
|
].join('\n'));
|
|
1304
1487
|
return { handled: true, config };
|
|
1305
1488
|
}
|
|
@@ -1312,13 +1495,29 @@ function createAdminCommandHandler(deps) {
|
|
|
1312
1495
|
|
|
1313
1496
|
setDefaultEngine(arg); // syncs distill model + providerMod.setEngine (no longer resets session model)
|
|
1314
1497
|
const distill = getDistillModel();
|
|
1315
|
-
|
|
1498
|
+
let freshCfg = loadConfig();
|
|
1499
|
+
if (boundProjectKey && freshCfg && freshCfg.projects && freshCfg.projects[boundProjectKey]) {
|
|
1500
|
+
const nextCfg = JSON.parse(JSON.stringify(freshCfg));
|
|
1501
|
+
nextCfg.projects[boundProjectKey].engine = arg;
|
|
1502
|
+
nextCfg.projects[boundProjectKey].model = resolveEngineModel(arg, nextCfg.daemon || {});
|
|
1503
|
+
writeConfigSafe(nextCfg);
|
|
1504
|
+
freshCfg = loadConfig();
|
|
1505
|
+
}
|
|
1316
1506
|
const freshDaemon = freshCfg.daemon || {};
|
|
1317
|
-
const syncedModel = resolveEngineModel(
|
|
1507
|
+
const syncedModel = resolveEngineModel(
|
|
1508
|
+
arg,
|
|
1509
|
+
freshDaemon,
|
|
1510
|
+
boundProjectKey && freshCfg.projects && freshCfg.projects[boundProjectKey]
|
|
1511
|
+
? freshCfg.projects[boundProjectKey].model
|
|
1512
|
+
: ''
|
|
1513
|
+
);
|
|
1318
1514
|
|
|
1319
|
-
// Auto-switch provider
|
|
1515
|
+
// Auto-switch provider only for Claude-compatible routing.
|
|
1516
|
+
// Codex auth is handled by `codex login` / `OPENAI_API_KEY`, not providers.yaml.
|
|
1320
1517
|
let providerNote = '';
|
|
1321
|
-
if (
|
|
1518
|
+
if (arg === 'codex') {
|
|
1519
|
+
providerNote = '\n🔌 Codex 认证: 使用 `codex login` 或 OPENAI_API_KEY(/provider 不参与 Codex 路由)';
|
|
1520
|
+
} else if (providerMod && preferredProvider) {
|
|
1322
1521
|
try {
|
|
1323
1522
|
providerMod.setActive(preferredProvider);
|
|
1324
1523
|
providerNote = `\n🔌 Provider 已同步: ${preferredProvider}`;
|
|
@@ -1329,8 +1528,11 @@ function createAdminCommandHandler(deps) {
|
|
|
1329
1528
|
}
|
|
1330
1529
|
}
|
|
1331
1530
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1531
|
+
const scopeNote = boundProjectKey
|
|
1532
|
+
? `\n📍 已同步当前 Agent: ${boundProjectKey}`
|
|
1533
|
+
: '';
|
|
1534
|
+
await bot.sendMessage(chatId, `✅ 引擎已切换: ${arg}\n🤖 会话模型: ${syncedModel}\n🧪 后台轻量模型: ${distill}${scopeNote}${providerNote}`);
|
|
1535
|
+
return { handled: true, config: freshCfg };
|
|
1334
1536
|
}
|
|
1335
1537
|
|
|
1336
1538
|
// /distill-model [name] — show or update distill model
|