Improve runtime status clarity and direct answers

---
Build: pass | Tests: FAIL — Tests  1 failed | 2043 passed (2044)
This commit is contained in:
Operator & Codex 2026-04-29 08:51:35 +02:00
parent 29c472f7f7
commit de88930ffd
5 changed files with 118 additions and 16 deletions

View file

@ -415,6 +415,7 @@ export async function runJailAgent(
const runtimeInfo =
`Rule: Your current LLM runtime is provider=${effectiveProvider || 'unknown'}, model=${effectiveModel || 'unknown'}. ` +
`State this accurately if asked — do not guess or substitute another model name.\n` +
`Rule: Do not end your reply with a promise of future inspection or follow-up unless you are actually providing that follow-up in the same message. If there is no real async job, answer directly now.\n` +
`Rule: The Mevy service runs as a FreeBSD rc.d service and executes \`node dist/index.js\` (not \`node src/index.ts\`). ` +
`Do not tell the operator to run \`just dev\` or start the service manually unless explicitly asked; instead, instruct to check \`sudo service ${PLATFORM_SERVICE_NAME} status\` and use \`sudo service ${PLATFORM_SERVICE_NAME} restart\` when needed.\n` +
`Rule: Text-to-speech uses the \`edge-tts\` CLI provided by the repo wrapper at \`/home/mevy/mevy-ai/bin/edge-tts\`, which runs from a persistent uv venv at \`/home/mevy/mevy-ai/.venv-edge-tts\`. ` +

View file

@ -38,4 +38,11 @@ describe('pi-profile', () => {
expect(prompt).toContain('customer-safe summary');
expect(prompt).toContain('Session policy: persistent');
});
it('teaches operator mode to answer directly instead of promising follow-up', () => {
const profile = resolvePiTuiProfile({ profile: 'operator' });
const prompt = buildPiTuiProfilePrompt(profile.name, profile);
expect(prompt).toContain('Do not reply with placeholder lines like "let me inspect"');
expect(prompt).toContain('If you can answer now, answer now.');
});
});

View file

@ -80,6 +80,7 @@ export const PI_TUI_PROFILES: Record<PiTuiProfileName, PiTuiProfileDefinition> =
'Stay concise and operational.',
'Prefer immediate fixes over long explanations.',
'Always end your turn with a visible text reply. Never finish with only a thinking block — your final output must be readable text.',
'Do not reply with placeholder lines like "let me inspect" or "I will check" unless the findings are included in the same reply. If you can answer now, answer now.',
],
},
status: {

View file

@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
import {
augmentPickerModels,
buildStatusRuntimeLines,
getTtsModeForChat,
setTtsModeForChat,
clearTtsOverride,
@ -200,3 +201,40 @@ describe('augmentPickerModels', () => {
expect(models.map((m) => m.id)).toEqual(['openai/o3']);
});
});
describe('buildStatusRuntimeLines', () => {
it('shows default, chat override, heartbeat override, and drift clearly', () => {
const lines = buildStatusRuntimeLines({
defaultProvider: 'deepseek',
defaultModel: 'deepseek-chat',
heartbeatProvider: 'deepseek',
heartbeatModel: 'deepseek-v4-flash',
overrideProvider: 'openrouter',
overrideModel: 'openai/o3',
actualProvider: 'deepseek',
actualModel: 'deepseek-chat',
});
expect(lines).toEqual([
'Default runtime: <code>deepseek/deepseek-chat</code>',
'Chat runtime: <code>openrouter/openai/o3</code> (override)',
'Heartbeat runtime: <code>deepseek/deepseek-v4-flash</code>',
'Last actual: <code>deepseek/deepseek-chat</code> ⚠ drift',
]);
});
it('shows a default-backed chat runtime when there is no override', () => {
const lines = buildStatusRuntimeLines({
defaultProvider: 'deepseek',
defaultModel: 'deepseek-chat',
actualProvider: 'deepseek',
actualModel: 'deepseek-chat',
});
expect(lines).toEqual([
'Default runtime: <code>deepseek/deepseek-chat</code>',
'Chat runtime: <code>deepseek/deepseek-chat</code> (default)',
'Last actual: <code>deepseek/deepseek-chat</code>',
]);
});
});

View file

@ -14,6 +14,8 @@ import {
AGENT_BUDGET_PAUSE_PCT,
ASSISTANT_NAME,
CMS_WEBROOT,
HEARTBEAT_MODEL,
HEARTBEAT_PROVIDER,
PLATFORM_ID,
PLATFORM_SERVICE_NAME,
PI_TUI_MODEL,
@ -231,6 +233,56 @@ function getChatLastActualRuntimeLine(chatJid: string): string | null {
return `Last actual runtime: ${provider} / ${model}`;
}
export function buildStatusRuntimeLines(opts: {
defaultProvider: string;
defaultModel: string;
heartbeatProvider?: string;
heartbeatModel?: string;
overrideProvider?: string;
overrideModel?: string;
actualProvider?: string;
actualModel?: string;
}): string[] {
const defaultProvider = (opts.defaultProvider || '').trim() || 'default';
const defaultModel = (opts.defaultModel || '').trim() || 'default';
const overrideProvider = (opts.overrideProvider || '').trim();
const overrideModel = (opts.overrideModel || '').trim();
const actualProvider = (opts.actualProvider || '').trim();
const actualModel = (opts.actualModel || '').trim();
const heartbeatProvider = (opts.heartbeatProvider || '').trim();
const heartbeatModel = (opts.heartbeatModel || '').trim();
const effectiveProvider = overrideProvider || defaultProvider;
const effectiveModel = overrideModel || defaultModel;
const hasOverride = Boolean(overrideProvider || overrideModel);
const lines = [
`Default runtime: <code>${defaultProvider}/${defaultModel}</code>`,
`Chat runtime: <code>${effectiveProvider}/${effectiveModel}</code>${hasOverride ? ' (override)' : ' (default)'}`,
];
const effectiveHeartbeatProvider = heartbeatProvider || defaultProvider;
const effectiveHeartbeatModel = heartbeatModel || defaultModel;
const heartbeatDiffers =
effectiveHeartbeatProvider !== defaultProvider ||
effectiveHeartbeatModel !== defaultModel;
if (heartbeatDiffers) {
lines.push(
`Heartbeat runtime: <code>${effectiveHeartbeatProvider}/${effectiveHeartbeatModel}</code>`,
);
}
if (actualProvider && actualModel) {
const drifted =
actualProvider !== effectiveProvider || actualModel !== effectiveModel;
lines.push(
`Last actual: <code>${actualProvider}/${actualModel}</code>${drifted ? ' ⚠ drift' : ''}`,
);
}
return lines;
}
async function replyAuthFailure(ctxArg: any, message: string): Promise<void> {
try {
if (ctxArg.callbackQuery) {
@ -2004,23 +2056,19 @@ export async function handleStatusCommand(
const overrideModel = (group?.jailConfig?.model || '').trim();
const actualProvider = (group?.jailConfig?.lastRuntimeProvider || '').trim();
const actualModel = (group?.jailConfig?.lastRuntimeModel || '').trim();
const hasOverride = Boolean(overrideProvider || overrideModel);
const effectiveProvider =
overrideProvider || (defaultProvider || '').trim();
const effectiveModel = overrideModel || defaultModel;
if (effectiveProvider) {
if (defaultProvider) {
lines.push(
`Model: <code>${effectiveProvider}/${effectiveModel}</code>${hasOverride ? ' (override)' : ''}`,
...buildStatusRuntimeLines({
defaultProvider,
defaultModel,
heartbeatProvider: HEARTBEAT_PROVIDER,
heartbeatModel: HEARTBEAT_MODEL,
overrideProvider,
overrideModel,
actualProvider,
actualModel,
}),
);
if (hasOverride && defaultProvider) {
lines.push(
`Default model: <code>${defaultProvider}/${defaultModel}</code>`,
);
}
if (actualProvider && actualModel) {
lines.push(`Last actual: <code>${actualProvider}/${actualModel}</code>`);
}
}
const active = c.queue.getActiveCount();
@ -2031,7 +2079,14 @@ export async function handleStatusCommand(
lines.push(`Groups: ${Object.keys(groups).length} registered`);
} else {
if (defaultProvider) {
lines.push(`Model: <code>${defaultProvider}/${defaultModel}</code>`);
lines.push(
...buildStatusRuntimeLines({
defaultProvider,
defaultModel,
heartbeatProvider: HEARTBEAT_PROVIDER,
heartbeatModel: HEARTBEAT_MODEL,
}),
);
}
}