104 lines
4.3 KiB
TypeScript
104 lines
4.3 KiB
TypeScript
import { afterEach, describe, expect, it } from 'vitest';
|
|
import Fastify from 'fastify';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { addAgent } from '../../src/local/agents-store.js';
|
|
import { AgentRunRegistry } from '../../src/local/agent-run-registry.js';
|
|
import { agentEntityRoutes } from '../../src/local/routes/agents.js';
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe('Agent Center durable Fleet run integration', () => {
|
|
it('passes agentId, derives live state from the registry, and cancels only that run', async () => {
|
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-agent-durable-'));
|
|
tempDirs.push(dataDir);
|
|
const registry = new AgentRunRegistry(path.join(dataDir, 'agent-runs.json'));
|
|
const agent = addAgent(dataDir, {
|
|
name: 'Research agent',
|
|
goal: 'Research the workspace',
|
|
type: 'workspace',
|
|
personaId: 'researcher',
|
|
model: 'test-model',
|
|
autonomyLevel: 'guided',
|
|
workspaceIds: ['workspace-1'],
|
|
memoryScopes: ['personal', 'workspace'],
|
|
status: 'idle',
|
|
});
|
|
let receivedAgentId = '';
|
|
let cancelledRunId = '';
|
|
let legacyTraceStarts = 0;
|
|
let legacyPauses = 0;
|
|
const server = Fastify({ logger: false });
|
|
server.decorate('localConfig', { dataDir, port: 0, host: '127.0.0.1', litellmUrl: '' });
|
|
server.decorate('agentRunRegistry', registry);
|
|
server.decorate('traceStore', {
|
|
queryParsed: () => [],
|
|
start: () => { legacyTraceStarts++; return 1; },
|
|
} as never);
|
|
server.decorate('sessionManager', {
|
|
getActive: () => [],
|
|
pause: () => { legacyPauses++; return true; },
|
|
} as never);
|
|
server.post('/api/fleet/spawn', async (request, reply) => {
|
|
const body = request.body as { agentId: string; task: string; parentWorkspaceId: string };
|
|
receivedAgentId = body.agentId;
|
|
const room = registry.createRoom({
|
|
workspaceIds: [body.parentWorkspaceId], source: 'fleet', title: 'Agent run', task: body.task,
|
|
});
|
|
const run = registry.createWorker({
|
|
parentRunId: room.id,
|
|
workspaceId: body.parentWorkspaceId,
|
|
source: 'fleet',
|
|
executor: { kind: 'waggle_agent', agentId: body.agentId, personaId: 'researcher', model: 'test-model' },
|
|
title: 'Research agent',
|
|
task: body.task,
|
|
status: 'running',
|
|
capabilities: { cancel: true },
|
|
});
|
|
registry.registerControls(run.id, { cancel: () => { cancelledRunId = run.id; } });
|
|
return reply.code(202).send({
|
|
runId: run.id,
|
|
roomId: room.id,
|
|
sessionId: `spawn-${run.id}`,
|
|
workspaceId: body.parentWorkspaceId,
|
|
status: 'running',
|
|
statusUrl: `/api/agent-runs/${run.id}`,
|
|
resumable: false,
|
|
model: 'test-model',
|
|
});
|
|
});
|
|
await server.register(agentEntityRoutes);
|
|
|
|
const started = await server.inject({
|
|
method: 'POST', url: `/api/agents/${agent.id}/run`, payload: { input: 'Investigate now' },
|
|
});
|
|
expect(started.statusCode).toBe(200);
|
|
const startedBody = started.json() as { runId: string; roomId: string; resumable: boolean; statusUrl: string };
|
|
expect(receivedAgentId).toBe(agent.id);
|
|
expect(startedBody.runId).toMatch(/^run_/);
|
|
expect(startedBody.roomId).toMatch(/^room_/);
|
|
expect(startedBody.resumable).toBe(false);
|
|
expect(startedBody.statusUrl).toBe(`/api/agent-runs/${startedBody.runId}`);
|
|
expect(legacyTraceStarts).toBe(0);
|
|
|
|
const agentsWhileRunning = await server.inject({ method: 'GET', url: '/api/agents' });
|
|
expect(agentsWhileRunning.json().agents[0].status).toBe('running');
|
|
|
|
const stopped = await server.inject({ method: 'POST', url: `/api/agents/${agent.id}/pause` });
|
|
expect(stopped.statusCode).toBe(200);
|
|
expect(stopped.json()).toMatchObject({ ok: true, paused: 0, cancelled: 1, runId: startedBody.runId });
|
|
expect(cancelledRunId).toBe(startedBody.runId);
|
|
expect(registry.get(startedBody.runId)?.status).toBe('cancelled');
|
|
expect(legacyPauses).toBe(0);
|
|
|
|
const agentsAfter = await server.inject({ method: 'GET', url: '/api/agents' });
|
|
expect(agentsAfter.json().agents[0].status).toBe('completed');
|
|
await server.close();
|
|
});
|
|
});
|