mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 16:20:45 +08:00
feat: add agent preset chat commands
This commit is contained in:
parent
2ad6f1a44e
commit
eea29fe296
33 changed files with 1519 additions and 234 deletions
|
|
@ -18,6 +18,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from '../shared/model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from '../shared/preset-command.mjs';
|
||||
import { runWorkspaceCommand } from '../shared/workspace-command.mjs';
|
||||
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
||||
import {
|
||||
|
|
@ -44,6 +48,10 @@ const HELP_TEXT = [
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
@ -336,7 +344,9 @@ export class DingtalkHarnessBridge {
|
|||
const commandText = nonEmptyString(promptMessage.content) ?? '';
|
||||
const commandRunner = isControlCommand(commandText)
|
||||
? runControlCommand
|
||||
: (isModelCommand(commandText) ? runModelCommand : null);
|
||||
: (isModelCommand(commandText)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
||||
const addressed = String(message.conversationType) !== '2' || message?.isInAtList === true;
|
||||
if (commandRunner && sessionWebhook && addressed) {
|
||||
let task;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from '../shared/model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from '../shared/preset-command.mjs';
|
||||
import { runWorkspaceCommand, resolveSessionListWorkspace, workspacePathSnapshot } from '../shared/workspace-command.mjs';
|
||||
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
||||
import {
|
||||
|
|
@ -88,6 +92,10 @@ const HELP_TEXT = [
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
@ -389,7 +397,9 @@ export class FeishuHarnessBridge {
|
|||
const commandText = nonEmptyString(commandMessage.content) ?? '';
|
||||
const commandRunner = isControlCommand(commandText)
|
||||
? runControlCommand
|
||||
: (isModelCommand(commandText) ? runModelCommand : null);
|
||||
: (isModelCommand(commandText)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
||||
if (commandRunner && addressed) {
|
||||
const processing = this.#processFastCommand(
|
||||
event,
|
||||
|
|
|
|||
|
|
@ -189,6 +189,10 @@ export function menuHelpText() {
|
|||
'/watch ID 或序号 关注会话(完成后推送)',
|
||||
'/compact 压缩上下文',
|
||||
'/workspace 绝对路径 切换工作区',
|
||||
'/presetlist 列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from '../shared/model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from '../shared/preset-command.mjs';
|
||||
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
||||
import {
|
||||
fetchImageBuffer,
|
||||
|
|
@ -49,6 +53,10 @@ const HELP_TEXT = [
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
@ -201,7 +209,9 @@ export class QqHarnessBridge {
|
|||
const commandText = safeText(message);
|
||||
const commandRunner = isControlCommand(commandText)
|
||||
? runControlCommand
|
||||
: (isModelCommand(commandText) ? runModelCommand : null);
|
||||
: (isModelCommand(commandText)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
||||
const allowed = this.#ownerUserOpenid === '*' || sender === this.#ownerUserOpenid;
|
||||
const addressed = message.kind !== 'group'
|
||||
|| message.rawEventType === 'GROUP_AT_MESSAGE_CREATE';
|
||||
|
|
|
|||
|
|
@ -535,6 +535,19 @@ function resolveAgentPresetCatalog(catalog) {
|
|||
: normalizeAgentPresetCatalog(value);
|
||||
}
|
||||
|
||||
function unavailableAgentPreset() {
|
||||
const error = new Error('Agent Preset 不存在或不可用。');
|
||||
error.code = 'agent-preset-unavailable';
|
||||
return error;
|
||||
}
|
||||
|
||||
function assertCurrentBotScope(isCurrentScope) {
|
||||
if (isCurrentScope()) return;
|
||||
const error = new Error('找不到要修改的机器人。');
|
||||
error.code = 'workspace-bot-not-found';
|
||||
throw error;
|
||||
}
|
||||
|
||||
function decorateResult(workspaces, result, catalog) {
|
||||
const decorate = (value) => {
|
||||
const decorated = workspaces.decorateStatus(value);
|
||||
|
|
@ -580,14 +593,74 @@ export function observeBotWorkspaceRemovals(
|
|||
});
|
||||
}
|
||||
|
||||
export function createBotWorkspaceScope(harness, { botId, workspaces, state }) {
|
||||
export function createBotWorkspaceScope(
|
||||
harness,
|
||||
{ botId, workspaces, state, agentPresetCatalog } = {},
|
||||
) {
|
||||
if (!harness || !workspaces || !state) throw new TypeError('harness, workspaces, and state are required');
|
||||
const incarnation = workspaces.incarnationFor(botId);
|
||||
const isCurrentScope = () => workspaces.has(botId)
|
||||
&& workspaces.incarnationFor(botId) === incarnation;
|
||||
const presetSettings = async (catalog = agentPresetCatalog) => {
|
||||
let normalizedCatalog;
|
||||
try {
|
||||
normalizedCatalog = await resolveAgentPresetCatalog(catalog)
|
||||
?? normalizeAgentPresetCatalog(null);
|
||||
} catch (error) {
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
throw error;
|
||||
}
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
return {
|
||||
agentPreset: workspaces.agentPresetFor(botId),
|
||||
agentPresetCatalog: normalizedCatalog,
|
||||
};
|
||||
};
|
||||
const sessionGenerations = new Map();
|
||||
const scopedHarness = new Proxy(harness, {
|
||||
get(target, property) {
|
||||
if (property === 'agentPresetSettings') {
|
||||
return async (options = {}) => {
|
||||
options?.signal?.throwIfAborted();
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
const settings = await presetSettings();
|
||||
options?.signal?.throwIfAborted();
|
||||
return settings;
|
||||
};
|
||||
}
|
||||
if (property === 'updateAgentPreset') {
|
||||
return async (value, options = {}) => {
|
||||
options?.signal?.throwIfAborted();
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
const agentPreset = value === '--default' ? null : validateAgentPresetId(value);
|
||||
let catalog = null;
|
||||
if (agentPreset) {
|
||||
({ agentPresetCatalog: catalog } = await presetSettings());
|
||||
options?.signal?.throwIfAborted();
|
||||
if (!catalog.items.some((item) => item.id === agentPreset)) {
|
||||
throw unavailableAgentPreset();
|
||||
}
|
||||
}
|
||||
await workspaces.setAgentPreset(botId, agentPreset, { incarnation });
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
if (catalog) {
|
||||
return {
|
||||
agentPreset: workspaces.agentPresetFor(botId),
|
||||
agentPresetCatalog: catalog,
|
||||
};
|
||||
}
|
||||
try {
|
||||
return await presetSettings();
|
||||
} catch (error) {
|
||||
if (error?.code === 'workspace-bot-not-found') throw error;
|
||||
assertCurrentBotScope(isCurrentScope);
|
||||
return {
|
||||
agentPreset: workspaces.agentPresetFor(botId),
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(null),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
if (property === 'currentWorkspace') {
|
||||
return () => {
|
||||
if (!isCurrentScope()) {
|
||||
|
|
@ -912,9 +985,7 @@ export function createWorkspaceAwareController(controller, { workspaces, stateFo
|
|||
: null;
|
||||
if (normalizedAgentPreset && agentPresetCatalog
|
||||
&& !catalog?.items.some((item) => item.id === normalizedAgentPreset)) {
|
||||
const error = new Error('Agent Preset 不存在或不可用。');
|
||||
error.code = 'agent-preset-unavailable';
|
||||
throw error;
|
||||
throw unavailableAgentPreset();
|
||||
}
|
||||
await workspaces.setAgentPreset(botId, normalizedAgentPreset, { incarnation });
|
||||
return decorateResult(
|
||||
|
|
|
|||
305
src/channels/shared/preset-command.mjs
Normal file
305
src/channels/shared/preset-command.mjs
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
import {
|
||||
normalizeAgentPresetCatalog,
|
||||
normalizeAgentPresetId,
|
||||
} from './agent-preset.mjs';
|
||||
import { withSessionBindingLock } from './session-binding-lock.mjs';
|
||||
import { splitWorkspaceCommandMessage } from './workspace-command.mjs';
|
||||
import { WORKSPACE_SESSION_STALE } from './workspace-session.mjs';
|
||||
|
||||
const PRESET_COMMAND = /^\/preset(?=$|\s)/iu;
|
||||
const PRESET_LIST_COMMAND = /^\/presetlist(?=$|\s)/iu;
|
||||
const PRESET_LIST_USAGE = '用法:/presetlist(不带参数)';
|
||||
const PRESET_USAGE = [
|
||||
'用法:',
|
||||
'/preset 查看当前设置',
|
||||
'/preset <序号> 按最近一次 /presetlist 的序号选择',
|
||||
'/preset <ID> 按 Agent Preset ID 选择',
|
||||
'/preset id:<纯数字 ID> 选择纯数字 ID',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
].join('\n');
|
||||
const UNSAFE_DISPLAY_TEXT_GLOBAL = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]+/gu;
|
||||
const LIST_SNAPSHOTS = new WeakMap();
|
||||
export const PRESET_LIST_SNAPSHOT_TTL_MS = 15 * 60_000;
|
||||
export const PRESET_LIST_SNAPSHOT_MAX_ENTRIES = 256;
|
||||
|
||||
function commandResult(message) {
|
||||
return {
|
||||
handled: true,
|
||||
message,
|
||||
messages: splitWorkspaceCommandMessage(message),
|
||||
};
|
||||
}
|
||||
|
||||
function safeDisplayText(value) {
|
||||
if (typeof value !== 'string') return '';
|
||||
return value.replace(UNSAFE_DISPLAY_TEXT_GLOBAL, ' ').replace(/\s+/gu, ' ').trim();
|
||||
}
|
||||
|
||||
function rpcOptions(signal) {
|
||||
return signal ? { signal } : {};
|
||||
}
|
||||
|
||||
function normalizeSettings(value) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)
|
||||
|| !value.agentPresetCatalog || typeof value.agentPresetCatalog !== 'object'
|
||||
|| !Array.isArray(value.agentPresetCatalog.items)) {
|
||||
throw new TypeError('Harness returned invalid Agent Preset settings');
|
||||
}
|
||||
const agentPreset = value.agentPreset === null
|
||||
? null
|
||||
: normalizeAgentPresetId(value.agentPreset);
|
||||
if (value.agentPreset !== null && agentPreset === null) {
|
||||
throw new TypeError('Harness returned an invalid current Agent Preset');
|
||||
}
|
||||
return {
|
||||
agentPreset,
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
function presetItemText(item) {
|
||||
const label = safeDisplayText(item.label) || item.id;
|
||||
return `${label}(${item.id})`;
|
||||
}
|
||||
|
||||
function itemFor(catalog, id) {
|
||||
return catalog.items.find((item) => item.id === id) ?? null;
|
||||
}
|
||||
|
||||
function defaultDescription(catalog) {
|
||||
if (!catalog.defaultId) return '未设置或当前不可用';
|
||||
const item = itemFor(catalog, catalog.defaultId);
|
||||
return item
|
||||
? presetItemText(item)
|
||||
: `${catalog.defaultId}(当前不可用)`;
|
||||
}
|
||||
|
||||
function currentDescription(settings) {
|
||||
const { agentPreset, agentPresetCatalog: catalog } = settings;
|
||||
if (agentPreset === null) {
|
||||
const item = itemFor(catalog, catalog.defaultId);
|
||||
return item
|
||||
? `跟随 Host 默认:${presetItemText(item)}`
|
||||
: '跟随 Host 默认(Host 默认当前不可用)';
|
||||
}
|
||||
const item = itemFor(catalog, agentPreset);
|
||||
return item
|
||||
? presetItemText(item)
|
||||
: `${agentPreset}(已不可用)`;
|
||||
}
|
||||
|
||||
function formatCurrent(settings) {
|
||||
return [
|
||||
'当前机器人用于新会话的 Agent Preset:',
|
||||
currentDescription(settings),
|
||||
'',
|
||||
'已有会话不会受此设置影响。',
|
||||
'查看可用项:/presetlist',
|
||||
'恢复跟随 Host 默认:/preset --default',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function formatList(settings) {
|
||||
const { agentPreset, agentPresetCatalog: catalog } = settings;
|
||||
const lines = [
|
||||
'当前机器人用于新会话的 Agent Preset:',
|
||||
currentDescription(settings),
|
||||
'',
|
||||
`Host 默认:${defaultDescription(catalog)}`,
|
||||
'',
|
||||
`可用 Agent Preset(${catalog.items.length}):`,
|
||||
];
|
||||
if (catalog.items.length === 0) {
|
||||
lines.push('当前没有可用 Agent Preset。');
|
||||
} else {
|
||||
catalog.items.forEach((item, index) => {
|
||||
const markers = [];
|
||||
if (item.id === catalog.defaultId) markers.push('Host 默认');
|
||||
if (item.id === agentPreset) markers.push('当前选择');
|
||||
if (agentPreset === null && item.id === catalog.defaultId) markers.push('当前生效');
|
||||
const annotation = markers.length > 0 ? `(${markers.join(',')})` : '';
|
||||
lines.push(`${index + 1}. ${presetItemText(item)}${annotation}`);
|
||||
});
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'选择:/preset <序号或 ID>',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'恢复跟随 Host 默认:/preset --default',
|
||||
);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function formatUpdated(settings) {
|
||||
return [
|
||||
'当前机器人用于新会话的 Agent Preset 已设置为:',
|
||||
currentDescription(settings),
|
||||
'',
|
||||
'已有会话不变。若当前聊天已有会话,请先发送 /new,再发送普通消息,才会使用新设置创建会话。',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function stateSnapshots(state, { create = false } = {}) {
|
||||
if ((typeof state !== 'object' || state === null) && typeof state !== 'function') return null;
|
||||
let snapshots = LIST_SNAPSHOTS.get(state);
|
||||
if (!snapshots && create) {
|
||||
snapshots = new Map();
|
||||
LIST_SNAPSHOTS.set(state, snapshots);
|
||||
}
|
||||
return snapshots ?? null;
|
||||
}
|
||||
|
||||
function pruneExpiredSnapshots(snapshots, now) {
|
||||
for (const [snapshotKey, snapshot] of snapshots) {
|
||||
if (snapshot.expiresAt <= now) snapshots.delete(snapshotKey);
|
||||
}
|
||||
}
|
||||
|
||||
function saveSnapshot(state, key, items) {
|
||||
const snapshots = stateSnapshots(state, { create: true });
|
||||
if (!snapshots) return;
|
||||
const now = Date.now();
|
||||
pruneExpiredSnapshots(snapshots, now);
|
||||
snapshots.delete(key);
|
||||
snapshots.set(key, {
|
||||
expiresAt: now + PRESET_LIST_SNAPSHOT_TTL_MS,
|
||||
ids: items.map((item) => item.id),
|
||||
});
|
||||
while (snapshots.size > PRESET_LIST_SNAPSHOT_MAX_ENTRIES) {
|
||||
const oldest = snapshots.keys().next();
|
||||
if (oldest.done) break;
|
||||
snapshots.delete(oldest.value);
|
||||
}
|
||||
}
|
||||
|
||||
function loadSnapshot(state, key) {
|
||||
const snapshots = stateSnapshots(state);
|
||||
const snapshot = snapshots?.get(key);
|
||||
if (!snapshots || !snapshot) return null;
|
||||
if (snapshot.expiresAt <= Date.now()) {
|
||||
snapshots.delete(key);
|
||||
return null;
|
||||
}
|
||||
snapshots.delete(key);
|
||||
snapshots.set(key, snapshot);
|
||||
return snapshot.ids;
|
||||
}
|
||||
|
||||
function presetFromSnapshot(state, key, requested) {
|
||||
if (!/^\d+$/u.test(requested)) return { numeric: false, id: null };
|
||||
const index = Number(requested);
|
||||
if (!Number.isSafeInteger(index) || index < 1) {
|
||||
return { numeric: true, error: 'Agent Preset 序号无效,请先执行 /presetlist。' };
|
||||
}
|
||||
const snapshot = loadSnapshot(state, key);
|
||||
if (!snapshot) {
|
||||
return { numeric: true, error: '请先执行 /presetlist,再按列表序号选择 Agent Preset。' };
|
||||
}
|
||||
const id = snapshot[index - 1];
|
||||
return id
|
||||
? { numeric: true, id }
|
||||
: { numeric: true, error: 'Agent Preset 序号不存在,请重新执行 /presetlist。' };
|
||||
}
|
||||
|
||||
function errorCode(error) {
|
||||
return error?.code ?? error?.failure?.code;
|
||||
}
|
||||
|
||||
function presetErrorMessage(error, action) {
|
||||
const code = errorCode(error);
|
||||
if (code === 'agent-preset-invalid') {
|
||||
return `Agent Preset ID 格式无效。\n${PRESET_USAGE}`;
|
||||
}
|
||||
if (code === 'agent-preset-unavailable') {
|
||||
return 'Agent Preset 不存在或当前不可用,请重新执行 /presetlist。';
|
||||
}
|
||||
if (code === WORKSPACE_SESSION_STALE || code === 'workspace-bot-not-found') {
|
||||
return '工作区或机器人状态已发生变化,请重试。';
|
||||
}
|
||||
if (code === 'cancelled' || error?.name === 'AbortError') {
|
||||
if (action === 'list') return '获取 Agent Preset 列表已取消。';
|
||||
if (action === 'current') return '获取 Agent Preset 设置已取消。';
|
||||
return 'Agent Preset 修改已取消。';
|
||||
}
|
||||
if (action === 'list') return '暂时无法获取 Agent Preset 列表,请稍后重试。';
|
||||
if (action === 'current') return '暂时无法获取 Agent Preset 设置,请稍后重试。';
|
||||
return 'Agent Preset 修改失败,请稍后重试。';
|
||||
}
|
||||
|
||||
async function settings(harness, options) {
|
||||
if (typeof harness?.agentPresetSettings !== 'function') {
|
||||
throw new TypeError('Harness does not support Agent Preset settings');
|
||||
}
|
||||
return normalizeSettings(await harness.agentPresetSettings(options));
|
||||
}
|
||||
|
||||
async function update(harness, value, options) {
|
||||
if (typeof harness?.updateAgentPreset !== 'function') {
|
||||
throw new TypeError('Harness does not support updating Agent Preset settings');
|
||||
}
|
||||
return normalizeSettings(await harness.updateAgentPreset(value, options));
|
||||
}
|
||||
|
||||
export function isPresetCommand(text) {
|
||||
if (typeof text !== 'string') return false;
|
||||
const command = text.trim();
|
||||
return PRESET_LIST_COMMAND.test(command) || PRESET_COMMAND.test(command);
|
||||
}
|
||||
|
||||
export async function runPresetCommand(text, harness, state, key, options = {}) {
|
||||
if (!isPresetCommand(text)) return null;
|
||||
const command = text.trim();
|
||||
if (options.hasImages) {
|
||||
return commandResult('Agent Preset 命令仅支持纯文字,请移除图片后重试。');
|
||||
}
|
||||
const requestOptions = rpcOptions(options.signal);
|
||||
|
||||
if (PRESET_LIST_COMMAND.test(command)) {
|
||||
if (!/^\/presetlist[ \t]*$/iu.test(command)) return commandResult(PRESET_LIST_USAGE);
|
||||
try {
|
||||
const current = await settings(harness, requestOptions);
|
||||
saveSnapshot(state, key, current.agentPresetCatalog.items);
|
||||
return commandResult(formatList(current));
|
||||
} catch (error) {
|
||||
return commandResult(presetErrorMessage(error, 'list'));
|
||||
}
|
||||
}
|
||||
|
||||
const match = /^\/preset(?:[ \t]+([^\s]+))?[ \t]*$/iu.exec(command);
|
||||
if (!match) return commandResult(PRESET_USAGE);
|
||||
const requested = match[1];
|
||||
if (!requested) {
|
||||
try {
|
||||
return commandResult(formatCurrent(await settings(harness, requestOptions)));
|
||||
} catch (error) {
|
||||
return commandResult(presetErrorMessage(error, 'current'));
|
||||
}
|
||||
}
|
||||
|
||||
let selected;
|
||||
if (requested.toLowerCase() === '--default') {
|
||||
selected = null;
|
||||
} else {
|
||||
const explicitNumericId = /^id:(\d+)$/iu.exec(requested);
|
||||
if (explicitNumericId) {
|
||||
selected = explicitNumericId[1];
|
||||
} else {
|
||||
const fromSnapshot = presetFromSnapshot(state, key, requested);
|
||||
if (fromSnapshot.numeric) {
|
||||
if (fromSnapshot.error) return commandResult(fromSnapshot.error);
|
||||
selected = fromSnapshot.id;
|
||||
} else {
|
||||
selected = normalizeAgentPresetId(requested);
|
||||
if (!selected) return commandResult(`Agent Preset ID 格式无效。\n${PRESET_USAGE}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await withSessionBindingLock(state, key, async () => (
|
||||
commandResult(formatUpdated(await update(harness, selected, requestOptions)))
|
||||
));
|
||||
} catch (error) {
|
||||
return commandResult(presetErrorMessage(error, 'update'));
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from './model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from './preset-command.mjs';
|
||||
import { askInWorkspaceSession } from './workspace-session.mjs';
|
||||
import { HarnessApprovalQueue } from './harness-approval.mjs';
|
||||
import {
|
||||
|
|
@ -122,7 +126,9 @@ export class TextHarnessBridge {
|
|||
const text = cleanText(normalized.content);
|
||||
const commandRunner = isControlCommand(text)
|
||||
? runControlCommand
|
||||
: (isModelCommand(text) ? runModelCommand : null);
|
||||
: (isModelCommand(text)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(text) ? runPresetCommand : null));
|
||||
if (commandRunner && (normalized.kind !== 'group' || normalized.addressed === true)) {
|
||||
let task;
|
||||
task = this.#processFastCommand(
|
||||
|
|
@ -319,6 +325,10 @@ export class TextHarnessBridge {
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ export const TELEGRAM_COMMAND_MENU = Object.freeze([
|
|||
{ command: 'session', description: '将当前聊天绑定到指定会话' },
|
||||
{ command: 'models', description: '按序号列出所有可用模型' },
|
||||
{ command: 'model', description: '查看或切换当前会话模型' },
|
||||
{ command: 'presetlist', description: '列出可用 Agent Preset' },
|
||||
{ command: 'preset', description: '查看或设置新会话 Agent Preset' },
|
||||
{ command: 'stop', description: '停止当前任务' },
|
||||
{ command: 'steer', description: '纠偏当前任务' },
|
||||
{ command: 'status', description: '检查连接状态' },
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from '../shared/model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from '../shared/preset-command.mjs';
|
||||
import { runWorkspaceCommand } from '../shared/workspace-command.mjs';
|
||||
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
||||
import {
|
||||
|
|
@ -37,6 +41,10 @@ const HELP_TEXT = [
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
@ -292,7 +300,9 @@ export class WecomHarnessBridge {
|
|||
const commandText = nonEmptyString(commandMessage.content) ?? '';
|
||||
const commandRunner = isControlCommand(commandText)
|
||||
? runControlCommand
|
||||
: (isModelCommand(commandText) ? runModelCommand : null);
|
||||
: (isModelCommand(commandText)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
||||
if (commandRunner) {
|
||||
let task;
|
||||
task = this.#processFastCommand(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ import {
|
|||
isModelCommand,
|
||||
runModelCommand,
|
||||
} from '../shared/model-command.mjs';
|
||||
import {
|
||||
isPresetCommand,
|
||||
runPresetCommand,
|
||||
} from '../shared/preset-command.mjs';
|
||||
import { runWorkspaceCommand } from '../shared/workspace-command.mjs';
|
||||
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
||||
import {
|
||||
|
|
@ -45,6 +49,10 @@ const HELP_TEXT = [
|
|||
'/models 按序号列出所有可用模型',
|
||||
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
||||
'示例:先发 /models,再发 /model 2',
|
||||
'/presetlist 按序号列出可用 Agent Preset',
|
||||
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
||||
'纯数字 ID:/preset id:<ID>',
|
||||
'/preset --default 跟随 Host 默认',
|
||||
'/stop 停止当前任务',
|
||||
'/steer 补充指令 纠偏当前任务',
|
||||
'/status 检查连接状态',
|
||||
|
|
@ -166,7 +174,9 @@ export class WeixinHarnessBridge {
|
|||
const commandText = nonEmptyString(extractWeixinText(message)) ?? '';
|
||||
const commandRunner = isControlCommand(commandText)
|
||||
? runControlCommand
|
||||
: (isModelCommand(commandText) ? runModelCommand : null);
|
||||
: (isModelCommand(commandText)
|
||||
? runModelCommand
|
||||
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
||||
if (commandRunner && sender === this.#ownerUserId) {
|
||||
let task;
|
||||
task = this.#processFastCommand(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue