mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-10 20:55:59 +08:00
feat: add session and preset list aliases
This commit is contained in:
parent
92ec91b7bd
commit
98c1e48271
21 changed files with 112 additions and 58 deletions
|
|
@ -3768,7 +3768,7 @@ test('preset card selection does not expose internal update errors', async () =>
|
|||
|
||||
function cards(messages) { return messages.filter((m) => m.msgType === 'interactive'); }
|
||||
|
||||
test('session list paginates by page number across 25 sessions', async () => {
|
||||
test('/sessions alias uses the interactive session list and paginates across 25 sessions', async () => {
|
||||
const fixture = stateFixture();
|
||||
const sent = [];
|
||||
const bridge = new FeishuHarnessBridge({
|
||||
|
|
@ -3782,7 +3782,7 @@ test('session list paginates by page number across 25 sessions', async () => {
|
|||
allowedSenderOpenIds: new Set(['ou_owner']),
|
||||
});
|
||||
|
||||
await bridge.accept(event('sessions-open', '/sessionlist', { senderOpenId: 'ou_owner' }));
|
||||
await bridge.accept(event('sessions-open', ' /SESSIONS ', { senderOpenId: 'ou_owner' }));
|
||||
await bridge.waitForIdle();
|
||||
assert.equal(cards(sent).length, 1);
|
||||
const page0 = cards(sent).at(-1).content;
|
||||
|
|
@ -3870,7 +3870,7 @@ test('session pagination preserves an explicitly selected workspace', async () =
|
|||
allowedSenderOpenIds: new Set(['ou_owner']),
|
||||
});
|
||||
|
||||
await bridge.accept(event('selected-workspace-open', '/sessionlist 2', { senderOpenId: 'ou_owner' }));
|
||||
await bridge.accept(event('selected-workspace-open', '/sessions 2', { senderOpenId: 'ou_owner' }));
|
||||
await bridge.waitForIdle();
|
||||
assert.equal(useActionsFromCard(cards(sent).at(-1).content)[0], 'selected-01');
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ test('menu exposes the increased command set and keeps permission completion num
|
|||
test('menu and card help advertise Agent Preset, reasoning, and batch commands', () => {
|
||||
const help = menuHelpText();
|
||||
assert.match(help, /\/presetlist/);
|
||||
assert.match(help, /\/presets/);
|
||||
assert.match(help, /\/sessions/);
|
||||
assert.match(help, /\/preset \[序号或完整ID\]/);
|
||||
assert.match(help, /\/preset id:<ID>/);
|
||||
assert.match(help, /\/preset --default/);
|
||||
|
|
@ -79,6 +81,8 @@ test('menu and card help advertise Agent Preset, reasoning, and batch commands',
|
|||
assert.match(help, /\/version/);
|
||||
|
||||
const card = helpCard();
|
||||
assert.match(card, /\/presets/);
|
||||
assert.match(card, /\/sessions/);
|
||||
assert.match(card, /\/reasoninglist/);
|
||||
assert.match(card, /\/reasonings/);
|
||||
assert.match(card, /\/reasoning \[序号、等级ID或 --default\]/);
|
||||
|
|
|
|||
|
|
@ -218,9 +218,10 @@ test('Telegram API registers the command menu and commands-type menu button', as
|
|||
await api.setChatMenuButton();
|
||||
assert.equal(calls.length, 2);
|
||||
assert.deepEqual(
|
||||
TELEGRAM_COMMAND_MENU.filter(({ command }) => command === 'presetlist' || command === 'preset'),
|
||||
TELEGRAM_COMMAND_MENU.filter(({ command }) => ['presetlist', 'presets', 'preset'].includes(command)),
|
||||
[
|
||||
{ command: 'presetlist', description: '列出可用 Agent Preset' },
|
||||
{ command: 'presets', description: '列出可用 Agent Preset' },
|
||||
{ command: 'preset', description: '查看或设置新会话 Agent Preset' },
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,20 +46,35 @@ function fixture({
|
|||
return { calls, harness, state: {}, selected: () => selected };
|
||||
}
|
||||
|
||||
test('isPresetCommand recognizes only /presetlist and /preset command prefixes', () => {
|
||||
test('isPresetCommand recognizes /presets, /presetlist, and /preset command prefixes', () => {
|
||||
for (const command of [
|
||||
'/presetlist', ' /PRESETLIST ', '/presetlist ignored',
|
||||
'/presetlist', ' /PRESETLIST ', '/presetlist ignored', '/presets', ' /PRESETS ',
|
||||
'/preset', '/PrEsEt coding',
|
||||
]) {
|
||||
assert.equal(isPresetCommand(command), true, command);
|
||||
}
|
||||
for (const value of [
|
||||
null, '', 'preset', '/presetx', '/presetlisting', 'hello /preset',
|
||||
null, '', 'preset', '/presetx', '/presetlisting', '/presetss', 'hello /preset',
|
||||
]) {
|
||||
assert.equal(isPresetCommand(value), false, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
test('/presets reuses /presetlist validation, listing, and snapshot behavior', async () => {
|
||||
const { calls, harness, state } = fixture({ agentPreset: 'coding' });
|
||||
const listed = await runPresetCommand(' /PRESETS ', harness, state, 'direct:alias');
|
||||
|
||||
assert.match(listed.message, /2\. Coding(coding)(当前选择)/);
|
||||
assert.deepEqual(calls, [['agentPresetSettings', {}]]);
|
||||
|
||||
const selected = await runPresetCommand('/preset 1', harness, state, 'direct:alias');
|
||||
assert.match(selected.message, /Standard(standard)/);
|
||||
assert.deepEqual(calls.at(-1), ['updateAgentPreset', 'standard', {}]);
|
||||
|
||||
const invalid = await runPresetCommand('/presets unexpected', harness, state, 'direct:alias');
|
||||
assert.match(invalid.message, /用法:\/presetlist/);
|
||||
});
|
||||
|
||||
test('/presetlist dynamically lists, annotates, and snapshots presets without updating', async () => {
|
||||
const { calls, harness, state } = fixture({ agentPreset: 'coding' });
|
||||
const signal = new AbortController().signal;
|
||||
|
|
|
|||
|
|
@ -1068,6 +1068,30 @@ test('/sessionlist supports the current workspace, list numbers, and absolute pa
|
|||
assert.equal(workspaceListCalls, 1, 'only numeric selection needs the workspace registry order');
|
||||
});
|
||||
|
||||
test('/sessions reuses /sessionlist parsing for current, numbered, and absolute workspaces', async (t) => {
|
||||
const { defaultWorkspace, alternateWorkspace } = await fixture(t);
|
||||
const calls = [];
|
||||
const harness = {
|
||||
currentWorkspace() { return defaultWorkspace; },
|
||||
async listWorkspaces() { return [alternateWorkspace]; },
|
||||
async listWorkspaceSessions(workspace) {
|
||||
calls.push(workspace);
|
||||
return { workspace, sessions: [] };
|
||||
},
|
||||
};
|
||||
|
||||
await runWorkspaceCommand(' /SESSIONS ', harness);
|
||||
await runWorkspaceCommand('/sessions 2', harness);
|
||||
await runWorkspaceCommand(`/sessions ${alternateWorkspace}`, harness);
|
||||
|
||||
assert.deepEqual(calls, [defaultWorkspace, alternateWorkspace, alternateWorkspace]);
|
||||
assert.equal(await runWorkspaceCommand('/sessionsx', harness), null);
|
||||
assert.match(
|
||||
(await runWorkspaceCommand('/sessions relative/path', harness)).message,
|
||||
/工作区必须是绝对路径/,
|
||||
);
|
||||
});
|
||||
|
||||
test('/sessionlist returns actionable and safe errors', async (t) => {
|
||||
const { root, defaultWorkspace } = await fixture(t);
|
||||
const file = join(root, 'not-a-workspace.txt');
|
||||
|
|
@ -1226,7 +1250,7 @@ test('all nine channel bridge families advertise and fan out workspace command r
|
|||
for (const file of bridgeFiles) {
|
||||
const source = await readFile(new URL(file, import.meta.url), 'utf8');
|
||||
assert.match(source, /\/workspacelist 列出工作区绝对路径/);
|
||||
assert.match(source, /\/sessionlist \[工作区序号或绝对路径\] 列出会话 ID 和标题/);
|
||||
assert.match(source, /\/sessionlist 或 \/sessions \[工作区序号或绝对路径\] 列出会话 ID 和标题/);
|
||||
assert.match(source, /workspaceCommand\.messages \?\? \[workspaceCommand\.message\]/);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue