forked from mosaicstack/stack
- PreferencesService: platform defaults, user overrides, IMMUTABLE_KEYS enforcement - PreferencesController: GET /api/preferences, POST /api/preferences, DELETE /api/preferences/:key - PreferencesModule: global module exporting PreferencesService and SystemOverrideService - SystemOverrideService: Valkey-backed session-scoped system prompt override with 5-min TTL + renew - CommandRegistryService: register /system command (socket execution) - CommandExecutorService: handle /system command via SystemOverrideService - AgentService: inject system override before each prompt turn, renew TTL; store userId in session - ChatGateway: pass userId when creating agent sessions - PreferencesService unit tests: 11 tests covering defaults, overrides, enforcement wins, immutable key errors Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
194 lines
4.7 KiB
TypeScript
194 lines
4.7 KiB
TypeScript
import { Injectable, type OnModuleInit } from '@nestjs/common';
|
|
import type { CommandDef, CommandManifest } from '@mosaic/types';
|
|
|
|
@Injectable()
|
|
export class CommandRegistryService implements OnModuleInit {
|
|
private readonly commands: CommandDef[] = [];
|
|
|
|
registerCommand(def: CommandDef): void {
|
|
const existing = this.commands.findIndex((c) => c.name === def.name);
|
|
if (existing >= 0) {
|
|
this.commands[existing] = def;
|
|
} else {
|
|
this.commands.push(def);
|
|
}
|
|
}
|
|
|
|
registerCommands(defs: CommandDef[]): void {
|
|
for (const def of defs) {
|
|
this.registerCommand(def);
|
|
}
|
|
}
|
|
|
|
getManifest(): CommandManifest {
|
|
return {
|
|
version: 1,
|
|
commands: [...this.commands],
|
|
skills: [],
|
|
};
|
|
}
|
|
|
|
onModuleInit(): void {
|
|
this.registerCommands([
|
|
{
|
|
name: 'model',
|
|
description: 'Switch the active model',
|
|
aliases: ['m'],
|
|
args: [
|
|
{
|
|
name: 'model-name',
|
|
type: 'string',
|
|
optional: false,
|
|
description: 'Model name to switch to',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'thinking',
|
|
description: 'Set thinking level (none/low/medium/high/auto)',
|
|
aliases: ['t'],
|
|
args: [
|
|
{
|
|
name: 'level',
|
|
type: 'enum',
|
|
optional: false,
|
|
values: ['none', 'low', 'medium', 'high', 'auto'],
|
|
description: 'Thinking level',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'new',
|
|
description: 'Start a new conversation',
|
|
aliases: ['n'],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'clear',
|
|
description: 'Clear conversation context and GC session artifacts',
|
|
aliases: [],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'compact',
|
|
description: 'Request context compaction',
|
|
aliases: [],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'retry',
|
|
description: 'Retry the last message',
|
|
aliases: [],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'rename',
|
|
description: 'Rename current conversation',
|
|
aliases: [],
|
|
args: [
|
|
{ name: 'name', type: 'string', optional: false, description: 'New conversation name' },
|
|
],
|
|
scope: 'core',
|
|
execution: 'rest',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'history',
|
|
description: 'Show conversation history',
|
|
aliases: [],
|
|
args: [
|
|
{
|
|
name: 'limit',
|
|
type: 'string',
|
|
optional: true,
|
|
description: 'Number of messages to show',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'rest',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'export',
|
|
description: 'Export conversation to markdown or JSON',
|
|
aliases: [],
|
|
args: [
|
|
{
|
|
name: 'format',
|
|
type: 'enum',
|
|
optional: true,
|
|
values: ['md', 'json'],
|
|
description: 'Export format',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'rest',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'preferences',
|
|
description: 'View or set user preferences',
|
|
aliases: ['pref'],
|
|
args: [
|
|
{
|
|
name: 'action',
|
|
type: 'enum',
|
|
optional: true,
|
|
values: ['show', 'set', 'reset'],
|
|
description: 'Action to perform',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'rest',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'system',
|
|
description: 'Set session-scoped system prompt override',
|
|
aliases: [],
|
|
args: [
|
|
{
|
|
name: 'override',
|
|
type: 'string',
|
|
optional: false,
|
|
description: 'System prompt text to inject for this session',
|
|
},
|
|
],
|
|
scope: 'core',
|
|
execution: 'socket',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'status',
|
|
description: 'Show session and connection status',
|
|
aliases: ['s'],
|
|
scope: 'core',
|
|
execution: 'hybrid',
|
|
available: true,
|
|
},
|
|
{
|
|
name: 'help',
|
|
description: 'Show available commands',
|
|
aliases: ['h'],
|
|
scope: 'core',
|
|
execution: 'local',
|
|
available: true,
|
|
},
|
|
]);
|
|
}
|
|
}
|