agent-ecosystem/test/main/services/team/RuntimeDiagnosticClassifier.test.ts
2026-05-18 01:57:16 +03:00

147 lines
4.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
classifyRuntimeDiagnostic,
selectRuntimeDiagnosticClassification,
} from '../../../../src/main/services/team/runtime/RuntimeDiagnosticClassifier';
describe('RuntimeDiagnosticClassifier', () => {
it('selects disk-full errors over aborted and empty OpenCode noise', () => {
const selected = selectRuntimeDiagnosticClassification([
'Latest assistant message msg_1 failed with MessageAbortedError - Aborted',
'empty_assistant_turn',
"OpenCode message bridge failed: ENOSPC: no space left on device, open '/tmp/.auth.json.tmp'",
]);
expect(selected).toMatchObject({
reasonCode: 'filesystem_error',
normalizedMessage: 'Local disk is full (ENOSPC). Free disk space and retry OpenCode delivery.',
actionRequired: true,
generic: false,
});
});
it('selects quota errors over empty assistant turns', () => {
const selected = selectRuntimeDiagnosticClassification([
'empty_assistant_turn',
'Latest assistant message msg_2 failed with APIError - Insufficient credits. Add more using https://openrouter.ai/settings/credits',
]);
expect(selected).toMatchObject({
reasonCode: 'quota_exhausted',
normalizedMessage:
'Insufficient credits. Add more using https://openrouter.ai/settings/credits',
actionRequired: true,
});
});
it('classifies OpenCode free usage retry status as quota exhausted', () => {
const selected = selectRuntimeDiagnosticClassification([
'empty_assistant_turn',
'OpenCode session status retry - attempt=1 - Free usage exceeded, subscribe to Go https://opencode.ai/go - next=2026-05-18T00:00:00.267Z',
]);
expect(selected).toMatchObject({
reasonCode: 'quota_exhausted',
normalizedMessage:
'OpenCode session status retry - attempt=1 - Free usage exceeded, subscribe to Go https://opencode.ai/go - next=2026-05-18T00:00:00.267Z',
actionRequired: true,
});
});
it('selects auth errors over bridge timeouts', () => {
const selected = selectRuntimeDiagnosticClassification([
'OpenCode bridge command timed out',
'authentication_failed: invalid API key',
]);
expect(selected).toMatchObject({
reasonCode: 'auth_error',
normalizedMessage: 'authentication_failed: invalid API key',
actionRequired: true,
});
});
it('classifies OpenCode bridge outcome timeouts as backend delivery state', () => {
expect(
classifyRuntimeDiagnostic('opencode_prompt_acceptance_unknown_after_bridge_timeout')
).toMatchObject({
reasonCode: 'backend_error',
normalizedMessage: 'OpenCode bridge outcome unknown after timeout, retrying/observing.',
generic: true,
actionRequired: false,
});
});
it('keeps pure empty assistant turns as generic backend fallback', () => {
expect(classifyRuntimeDiagnostic('empty_assistant_turn')).toMatchObject({
reasonCode: 'backend_error',
normalizedMessage: 'empty_assistant_turn',
generic: true,
actionRequired: false,
});
});
it('keeps protocol proof failures above generic runtime noise', () => {
const selected = selectRuntimeDiagnosticClassification([
'OpenCode bridge command timed out',
'visible_reply_missing_task_refs',
]);
expect(selected).toMatchObject({
reasonCode: 'protocol_proof_missing',
normalizedMessage: 'visible_reply_missing_task_refs',
generic: true,
actionRequired: false,
});
});
it('does not classify message_send Not connected as protocol proof missing', () => {
expect(
classifyRuntimeDiagnostic(
'agent-teams_message_send returned Not connected while sending a visible reply'
)
).toMatchObject({
reasonCode: 'backend_error',
actionRequired: false,
});
});
it('keeps explicit proof-missing diagnostics narrow', () => {
expect(
classifyRuntimeDiagnostic(
'OpenCode used tools, but did not create a visible reply or task progress proof.'
)
).toMatchObject({
reasonCode: 'protocol_proof_missing',
generic: true,
});
});
it('keeps quota and auth diagnostics above proof-missing substrings in the same message', () => {
expect(
classifyRuntimeDiagnostic(
'Insufficient credits: OpenCode used tools, but did not create a visible reply or task progress proof.'
)
).toMatchObject({
reasonCode: 'quota_exhausted',
actionRequired: true,
});
expect(
classifyRuntimeDiagnostic(
'authentication_failed: visible_reply_missing_task_refs because API key is invalid'
)
).toMatchObject({
reasonCode: 'auth_error',
actionRequired: true,
});
});
it('keeps OpenCode bridge command timeout as backend state despite timeout tokens', () => {
expect(classifyRuntimeDiagnostic('OpenCode bridge command timed out')).toMatchObject({
reasonCode: 'backend_error',
generic: true,
});
});
});