Owner Commands
Manage your bot at runtime through slash commands in chat.
Overview
Owner commands let you monitor and control your AI assistant directly from your messaging app. The OwnerCommandsMiddleware intercepts slash commands from the configured owner before they reach the agent.
Setup
import { Gateway, ownerCommands } from '@cogitator-ai/channels';
const gateway: Gateway = new Gateway({
// ...
middleware: [
ownerCommands({
ownerIds: {
telegram: '123456789',
discord: '987654321',
},
onStatus: () => `Up 2h, 5 sessions, 847 messages`,
onSessions: () => `Active: @john (telegram), @jane (discord)`,
onModel: (model, forUser) => `Model switched to ${model}`,
onCompact: (target, threadId) =>
gateway.compactThread(target === 'current' ? threadId : target),
}),
],
});Available Commands
| Command | Level | Description |
|---|---|---|
/status | authorized | Show uptime, sessions, message count |
/sessions | authorized | List recent sessions |
/help | authorized | Show available commands |
/users | owner | List owners and approved users |
/model <name> [@user] | owner | Switch model globally or for a specific user |
/compact [target] | owner | Compact the current conversation (or target thread) |
/restart | owner | Restart the assistant |
Arguments may follow on the next line, and Telegram group syntax /status@your_bot is recognized. /help also lists /pair <code>, which is handled by the pairing / DM policy middleware, not by this one.
/model without an onModel callback only echoes Model set to: … and changes nothing; wire it to your runtime as RuntimeBuilder does. With RuntimeBuilder, /model gemini-3.5-flash @42 sets a model for one user (bare model names get the configured provider prefix) and /restart exits the process with code 78, which cogitator up answers by starting the assistant again (unless it runs with --no-restart-loop, where the assistant just stops).
Configuration
interface OwnerCommandsConfig {
ownerIds: Record<string, string>;
authorizedUserIds?: string[]; // 'telegram:42' or bare '42'
publicCommands?: string[]; // e.g. ['help'] — anyone may run these
onStatus?: () => string;
onSessions?: () => string;
onUsers?: () => string;
onCompact?: (target: string, threadId: string) => Promise<string>; // target is 'current' when omitted
onModel?: (model: string, forUser?: string) => string;
onRestart?: () => Promise<void>;
}Callback Handlers
Each command maps to an optional callback. If a callback is not provided, the command returns a default message. Implement callbacks to hook into your gateway's runtime state:
ownerCommands({
ownerIds: { telegram: process.env.OWNER_TG_ID! },
onStatus: () => {
const stats = gateway.stats;
return [
`Uptime: ${formatDuration(stats.uptime)}`,
`Sessions: ${stats.activeSessions} active / ${stats.totalSessions} total`,
`Messages: ${stats.messagesToday} today`,
].join('\n');
},
onModel: (model, forUser) => {
// update model configuration
return `Switched to ${model}${forUser ? ` for ${forUser}` : ''}`;
},
});Security
- Commands are only processed from users with the required level (
ownerIdsfor owner commands,authorizedUserIdsfor authorized ones,publicCommandsfor everyone) - With
RuntimeBuilder(cogitator up) every command is wired to the live runtime: stats, sessions, approved users, compaction, per-user model overrides and restart - Non-owners sending
/statusor other commands see them passed through to the agent as regular messages - Unknown slash commands (e.g.,
/random) pass through to the agent