agent-ecosystem/test/main/services/team/TeamMemberRuntimeAdvisoryService.test.ts

159 lines
4.9 KiB
TypeScript

import * as os from 'os';
import * as path from 'path';
import { afterEach, describe, expect, it } from 'vitest';
import * as fs from 'fs/promises';
import { TeamMemberRuntimeAdvisoryService } from '../../../../src/main/services/team/TeamMemberRuntimeAdvisoryService';
import { setClaudeBasePathOverride } from '../../../../src/main/utils/pathDecoder';
describe('TeamMemberRuntimeAdvisoryService', () => {
let tmpDir: string | null = null;
afterEach(async () => {
setClaudeBasePathOverride(null);
if (tmpDir) {
await fs.rm(tmpDir, { recursive: true, force: true });
tmpDir = null;
}
});
it('returns active sdk retry advisory for a teammate log', async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-team-advisory-'));
setClaudeBasePathOverride(tmpDir);
const teamName = 'signal-ops';
const projectPath = '/Users/test/proj';
const projectId = '-Users-test-proj';
const leadSessionId = 'lead-session';
await fs.mkdir(path.join(tmpDir, 'teams', teamName), { recursive: true });
await fs.writeFile(
path.join(tmpDir, 'teams', teamName, 'config.json'),
JSON.stringify({
name: teamName,
projectPath,
leadSessionId,
members: [
{ name: 'team-lead', agentType: 'team-lead' },
{ name: 'alice', agentType: 'general-purpose' },
],
}),
'utf8'
);
const projectRoot = path.join(tmpDir, 'projects', projectId);
await fs.mkdir(path.join(projectRoot, leadSessionId, 'subagents'), { recursive: true });
await fs.writeFile(
path.join(projectRoot, `${leadSessionId}.jsonl`),
`${JSON.stringify({
timestamp: '2026-01-01T00:00:00.000Z',
type: 'user',
message: { role: 'user', content: 'Start' },
})}\n`,
'utf8'
);
const nowIso = new Date().toISOString();
await fs.writeFile(
path.join(projectRoot, leadSessionId, 'subagents', 'agent-alice.jsonl'),
[
JSON.stringify({
timestamp: nowIso,
type: 'user',
message: { role: 'user', content: 'You are alice, a reviewer on team "signal-ops" (signal-ops).' },
}),
JSON.stringify({
timestamp: nowIso,
type: 'system',
subtype: 'api_error',
retryInMs: 45_000,
retryAttempt: 1,
maxRetries: 10,
error: {
error: {
error: {
message: 'Gemini cli backend error: capacity exceeded.',
},
},
},
}),
].join('\n') + '\n',
'utf8'
);
const service = new TeamMemberRuntimeAdvisoryService();
const advisory = await service.getMemberAdvisory(teamName, 'alice');
expect(advisory).not.toBeNull();
expect(advisory?.kind).toBe('sdk_retrying');
expect(advisory?.message).toContain('capacity exceeded');
});
it('ignores expired retry advisories', async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-team-advisory-'));
setClaudeBasePathOverride(tmpDir);
const teamName = 'signal-ops';
const projectPath = '/Users/test/proj';
const projectId = '-Users-test-proj';
const leadSessionId = 'lead-session';
await fs.mkdir(path.join(tmpDir, 'teams', teamName), { recursive: true });
await fs.writeFile(
path.join(tmpDir, 'teams', teamName, 'config.json'),
JSON.stringify({
name: teamName,
projectPath,
leadSessionId,
members: [
{ name: 'team-lead', agentType: 'team-lead' },
{ name: 'alice', agentType: 'general-purpose' },
],
}),
'utf8'
);
const projectRoot = path.join(tmpDir, 'projects', projectId);
await fs.mkdir(path.join(projectRoot, leadSessionId, 'subagents'), { recursive: true });
await fs.writeFile(
path.join(projectRoot, `${leadSessionId}.jsonl`),
`${JSON.stringify({
timestamp: '2026-01-01T00:00:00.000Z',
type: 'user',
message: { role: 'user', content: 'Start' },
})}\n`,
'utf8'
);
await fs.writeFile(
path.join(projectRoot, leadSessionId, 'subagents', 'agent-alice.jsonl'),
[
JSON.stringify({
timestamp: new Date(Date.now() - 60_000).toISOString(),
type: 'user',
message: { role: 'user', content: 'You are alice, a reviewer on team "signal-ops" (signal-ops).' },
}),
JSON.stringify({
timestamp: new Date(Date.now() - 60_000).toISOString(),
type: 'system',
subtype: 'api_error',
retryInMs: 5_000,
retryAttempt: 1,
maxRetries: 10,
error: {
error: {
error: {
message: 'Old retry window',
},
},
},
}),
].join('\n') + '\n',
'utf8'
);
const service = new TeamMemberRuntimeAdvisoryService();
await expect(service.getMemberAdvisory(teamName, 'alice')).resolves.toBeNull();
});
});