SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
f0634fb verified
Raw History Blame Contribute Delete
3.67 kB
import { Readable } from 'node:stream';
import type { Writable } from 'node:stream';
import type { IHostProcess } from '#/os/interface/hostProcess';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
IAgentTaskService,
} from '#/agent/task/task';
import {
SubagentTask,
type SubagentHandle,
} from '#/agent/tools/agent/subagent-task';
import { ProcessTask } from '#/agent/tools/os/bash/process-task';
import { createTestAgent, type TestAgentContext } from '../../harness';
import { createAgentTaskPersistence } from './stubs';
function registerProcess(
manager: IAgentTaskService,
proc: IHostProcess,
command: string,
description: string,
): string {
return manager.registerTask(new ProcessTask(proc, command, description));
}
function agentTask(
completion: Promise<{ result: string }>,
description: string,
): SubagentTask {
const handle: SubagentHandle = {
agentId: 'agent-child',
profileName: 'coder',
completion,
};
return new SubagentTask(
handle,
description,
new AbortController(),
);
}
function pendingProcess(): IHostProcess & { resolve(code: number): void } {
let resolveWait: (code: number) => void = () => {};
const waitPromise = new Promise<number>((resolve) => {
resolveWait = resolve;
});
let currentExitCode: number | null = null;
return {
_serviceBrand: undefined,
stdin: { write: vi.fn(), end: vi.fn() } as unknown as Writable,
stdout: Readable.from([]),
stderr: Readable.from([]),
pid: 54321,
get exitCode(): number | null {
return currentExitCode;
},
wait: () => waitPromise,
kill: vi.fn().mockResolvedValue(undefined) as IHostProcess['kill'],
dispose: vi.fn().mockResolvedValue(undefined) as IHostProcess['dispose'],
resolve(code: number): void {
currentExitCode = code;
resolveWait(code);
},
};
}
describe('background task id format', () => {
let ctx: TestAgentContext;
let background: IAgentTaskService;
beforeEach(() => {
ctx = createTestAgent();
background = ctx.get(IAgentTaskService);
});
afterEach(async () => {
try {
await ctx.expectResumeMatches();
} finally {
await ctx.dispose();
}
});
it('assigns bash-prefixed ids to process tasks', async () => {
const proc = pendingProcess();
const id = registerProcess(background, proc, 'sleep 60', 'process task');
expect(id).toMatch(/^bash-[0-9a-z]{8}$/);
expect(background.getTask(id)).toMatchObject({ taskId: id, kind: 'process' });
proc.resolve(0);
await background.wait(id);
});
it('assigns agent-prefixed ids to agent tasks', async () => {
let resolveCompletion!: (value: { result: string }) => void;
const completion = new Promise<{ result: string }>((resolve) => {
resolveCompletion = resolve;
});
const id = background.registerTask(
agentTask(completion, 'agent task'),
);
expect(id).toMatch(/^agent-[0-9a-z]{8}$/);
expect(background.getTask(id)).toMatchObject({ taskId: id, kind: 'agent' });
resolveCompletion({ result: 'done' });
await background.wait(id);
});
it('rejects malformed ids at the persistence path boundary', () => {
const persistence = createAgentTaskPersistence('/tmp/kimi-bg-id-test');
const rejected = [
'',
'x',
'-bash',
'BASH-12345678',
'bash_12345678',
'../escape',
'bash-1234567',
'bash-123456789',
'agent-ABCDEFGH',
'bg_12345678',
'a'.repeat(26),
];
for (const bad of rejected) {
expect(() => persistence.taskOutputFile(bad)).toThrow(/Invalid task id/);
}
});
});