SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

+695
View File
@@ -0,0 +1,695 @@
import { randomUUID } from 'node:crypto';
import { spawnSync } from 'node:child_process';
import {
chmod,
mkdir,
mkdtemp,
readFile,
readdir,
rename,
rm,
stat,
symlink,
writeFile,
} from 'node:fs/promises';
import { tmpdir, hostname } from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
createMcpHandler,
InMemoryTransport,
Server,
type CallToolResult,
} from '@modelcontextprotocol/server';
import { PersistentTasks, installTaskHandlers, supportsTasks, TASK_EXTENSION } from '../src/tasks';
import { makeHttpClient, type ActionClient } from '../src/client';
import { callTool } from '../src/tools';
import { createMcpServer } from '../src/server-factory';
const scopeId = 'a'.repeat(64);
const cleanup: Array<() => Promise<void>> = [];
const success: CallToolResult = { content: [{ type: 'text', text: 'completed' }] };
const input = {
method: 'elicitation/create' as const,
params: {
mode: 'form' as const,
message: 'Choose a value',
requestedSchema: {
type: 'object' as const,
properties: { value: { type: 'string' as const } },
},
},
};
afterEach(async () => {
for (const close of cleanup.splice(0).reverse()) {
await close();
}
vi.restoreAllMocks();
});
async function directory() {
const dir = await mkdtemp(path.join(tmpdir(), 'snowluma-tasks-'));
cleanup.push(() => rm(dir, { recursive: true, force: true }));
return dir;
}
async function openTasks(
dir?: string,
overrides: Partial<Parameters<typeof PersistentTasks.open>[1]> = {},
) {
const tasks = await PersistentTasks.open(dir ?? (await directory()), {
scopeId,
onError: vi.fn(),
...overrides,
});
cleanup.push(() => tasks.close());
return tasks;
}
async function finished(tasks: PersistentTasks, id: string) {
await vi.waitFor(() =>
expect(['completed', 'failed', 'cancelled']).toContain(tasks.get(id).status),
);
return tasks.get(id);
}
describe('durable task execution', () => {
it('does not begin a side effect after shutdown was already queued', async () => {
const tasks = await openTasks();
const execute = vi.fn(async () => success);
const started = tasks.start(execute);
const closing = tasks.close();
const [handle] = await Promise.all([started, closing]);
expect(execute).not.toHaveBeenCalled();
expect(tasks.get(handle.taskId)).toMatchObject({
status: 'failed',
statusMessage: expect.stringContaining('unknown'),
});
});
it('persists before executing, keeps terminal results across restart, and stores no callback arguments', async () => {
const dir = await directory();
const tasks = await openTasks(dir);
const handle = await tasks.start(async ({ taskId }) => {
const persisted = JSON.parse(
await readFile(path.join(dir, `${taskId}.task.json`), 'utf8'),
);
expect(persisted.task.status).toBe('working');
return success;
});
expect(handle).toMatchObject({ resultType: 'task', status: 'working', ttlMs: null });
expect(await finished(tasks, handle.taskId)).toMatchObject({
status: 'completed',
result: { ...success, resultType: 'complete' },
});
await tasks.close();
const reopened = await openTasks(dir);
expect(reopened.get(handle.taskId)).toMatchObject({ status: 'completed', result: success });
const record = JSON.parse(
await readFile(path.join(dir, `${handle.taskId}.task.json`), 'utf8'),
);
expect(Object.keys(record).sort()).toEqual(['task', 'version']);
if (process.platform !== 'win32') {
expect((await stat(dir)).mode & 0o777).toBe(0o700);
expect((await stat(path.join(dir, `${handle.taskId}.task.json`))).mode & 0o777).toBe(
0o600,
);
}
});
it('reports tool business failures as completed results rather than protocol failure', async () => {
const tasks = await openTasks();
const handle = await tasks.start(async () => ({ content: [], isError: true }));
expect(await finished(tasks, handle.taskId)).toMatchObject({
status: 'completed',
result: { isError: true },
});
});
it('does not persist raw exceptions or credentials', async () => {
const dir = await directory();
const tasks = await openTasks(dir);
const handle = await tasks.start(async () => {
throw new Error('Bearer private-secret');
});
expect(await finished(tasks, handle.taskId)).toMatchObject({
status: 'failed',
error: { code: -32603 },
});
expect(await readFile(path.join(dir, `${handle.taskId}.task.json`), 'utf8')).not.toContain(
'private-secret',
);
});
it('marks crash-interrupted records unknown without replaying work', async () => {
const dir = await directory();
const first = await openTasks(dir);
await first.close();
const id = randomUUID();
const createdAt = new Date().toISOString();
await writeFile(
path.join(dir, `${id}.task.json`),
JSON.stringify({
version: 1,
task: {
taskId: id,
status: 'working',
createdAt,
lastUpdatedAt: createdAt,
ttlMs: null,
pollIntervalMs: 1000,
},
}),
{ mode: 0o600 },
);
const reopened = await openTasks(dir);
expect(reopened.get(id)).toMatchObject({
status: 'failed',
error: { code: -32603, message: expect.stringContaining('not replayed') },
});
});
it('propagates cancellation to the execution and preserves a completed result in a race', async () => {
const tasks = await openTasks();
let observed!: AbortSignal;
const handle = await tasks.start(async ({ signal }) => {
observed = signal;
await new Promise<void>((resolve) =>
signal.addEventListener('abort', () => resolve(), { once: true }),
);
return success;
});
await tasks.cancel(handle.taskId);
expect(observed.aborted).toBe(true);
expect(await finished(tasks, handle.taskId)).toMatchObject({ status: 'completed' });
await tasks.cancel(handle.taskId);
expect(tasks.get(handle.taskId).status).toBe('completed');
});
it('settles abort-rejecting operations as cancelled and never re-executes', async () => {
const tasks = await openTasks();
const execute = vi.fn(async ({ signal }: { signal: AbortSignal }) => {
await new Promise((_, reject) =>
signal.addEventListener('abort', () => reject(signal.reason), { once: true }),
);
return success;
});
const handle = await tasks.start(execute);
await tasks.cancel(handle.taskId);
expect(await finished(tasks, handle.taskId)).toMatchObject({
status: 'cancelled',
statusMessage: expect.stringContaining('does not undo'),
});
expect(execute).toHaveBeenCalledTimes(1);
});
it('closes all running operations and leaves unknown outcomes readable after restart', async () => {
const dir = await directory();
const tasks = await openTasks(dir);
let signal!: AbortSignal;
const handle = await tasks.start(async (ctx) => {
signal = ctx.signal;
return new Promise<CallToolResult>(() => undefined);
});
await tasks.close();
expect(signal.aborted).toBe(true);
const reopened = await openTasks(dir);
expect(reopened.get(handle.taskId)).toMatchObject({
status: 'failed',
statusMessage: expect.stringContaining('unknown'),
});
await expect(tasks.start(async () => success)).rejects.toThrow('closed');
});
it('marks every aborted runner terminal even when callbacks settle during close', async () => {
const tasks = await openTasks();
const handles = await Promise.all(
[1, 2, 3].map(() =>
tasks.start(async ({ signal }) => {
await new Promise((_, reject) =>
signal.addEventListener('abort', () => reject(signal.reason), {
once: true,
}),
);
return success;
}),
),
);
await tasks.close();
for (const handle of handles) {
expect(tasks.get(handle.taskId).status).toBe('failed');
}
});
it('enforces active concurrency without deleting completed history', async () => {
const tasks = await openTasks(undefined, { maxConcurrent: 1 });
let resolve!: (value: CallToolResult) => void;
const first = await tasks.start(
() =>
new Promise<CallToolResult>((yes) => {
resolve = yes;
}),
);
await expect(tasks.start(async () => success)).rejects.toThrow('concurrency');
resolve(success);
await finished(tasks, first.taskId);
const second = await tasks.start(async () => success);
await finished(tasks, second.taskId);
const page = tasks.list({ limit: 1 });
expect(page.tasks).toHaveLength(1);
expect(page.tasks[0]).not.toHaveProperty('result');
expect(tasks.list({ cursor: page.nextCursor, limit: 1 }).tasks[0].taskId).toBe(
second.taskId,
);
});
});
describe('task input lifecycle', () => {
it('accepts partial input, ignores repeated keys, and resumes the same callback once', async () => {
const tasks = await openTasks();
const execute = vi.fn(async ({ requestInput }) => {
const responses = await requestInput({ first: input, second: input });
expect(responses).toMatchObject({
first: { action: 'accept', content: { value: 'one' } },
second: { action: 'decline' },
});
return success;
});
const handle = await tasks.start(execute);
await vi.waitFor(() => expect(tasks.get(handle.taskId).status).toBe('input_required'));
await tasks.update(handle.taskId, {
unknown: {},
first: { action: 'accept', content: { value: 'one' } },
});
expect(tasks.get(handle.taskId)).toMatchObject({
status: 'input_required',
inputRequests: { second: input },
});
expect(tasks.get(handle.taskId)).not.toHaveProperty('inputRequests.first');
await tasks.update(handle.taskId, {
first: { action: 'cancel' },
second: { action: 'decline' },
});
expect(await finished(tasks, handle.taskId)).toMatchObject({ status: 'completed' });
expect(execute).toHaveBeenCalledTimes(1);
});
it('rejects malformed matching input without losing the pending request', async () => {
const tasks = await openTasks();
const handle = await tasks.start(async ({ requestInput }) => {
await requestInput({ value: input });
return success;
});
await vi.waitFor(() => expect(tasks.get(handle.taskId).status).toBe('input_required'));
await expect(tasks.update(handle.taskId, { value: { action: 'wrong' } })).rejects.toThrow(
'Invalid task input response',
);
expect(tasks.get(handle.taskId).status).toBe('input_required');
await tasks.cancel(handle.taskId);
expect((await finished(tasks, handle.taskId)).status).toBe('cancelled');
});
it('never reuses input keys across rounds', async () => {
const tasks = await openTasks();
const handle = await tasks.start(async ({ requestInput }) => {
await requestInput({ value: input });
await requestInput({ value: input });
return success;
});
await vi.waitFor(() => expect(tasks.get(handle.taskId).status).toBe('input_required'));
await tasks.update(handle.taskId, { value: { action: 'decline' } });
expect((await finished(tasks, handle.taskId)).status).toBe('failed');
});
});
describe('task storage boundary', () => {
it('recovers a dead-process lease with an explicit diagnostic', async () => {
const dir = await directory();
const previous = spawnSync(process.execPath, ['-e', 'process.exit(0)']);
expect(previous.status).toBe(0);
const lease = path.join(dir, `.lease-${randomUUID()}.json`);
await writeFile(lease, JSON.stringify({ pid: previous.pid, host: hostname() }), {
mode: 0o600,
});
const onError = vi.fn();
await openTasks(dir, { onError });
expect(onError).toHaveBeenCalledWith(expect.any(Error), { event: 'recovered_stale_lock' });
await expect(stat(lease)).rejects.toMatchObject({ code: 'ENOENT' });
});
it('makes persistence failures visible and still aborts all work during shutdown', async () => {
const dir = await directory();
const tasks = await openTasks(dir);
let resolve!: (value: CallToolResult) => void;
const first = await tasks.start(
() =>
new Promise<CallToolResult>((yes) => {
resolve = yes;
}),
);
let pending!: AbortSignal;
const second = await tasks.start(async ({ signal }) => {
pending = signal;
return new Promise<CallToolResult>(() => undefined);
});
const file = path.join(dir, `${first.taskId}.task.json`);
await rename(file, `${file}.saved`);
await mkdir(file);
resolve(success);
await vi.waitFor(() => expect(() => tasks.get(first.taskId)).toThrow('persistence failed'));
expect(() => tasks.get(second.taskId)).toThrow('persistence failed');
await tasks.close();
expect(pending.aborted).toBe(true);
expect((await readdir(dir)).some((entry) => entry.startsWith('.lease-'))).toBe(false);
});
it('refuses concurrent owners and scope changes', async () => {
const dir = await directory();
const owner = await openTasks(dir);
await expect(PersistentTasks.open(dir, { scopeId })).rejects.toThrow('locked');
await owner.close();
await expect(PersistentTasks.open(dir, { scopeId: 'b'.repeat(64) })).rejects.toThrow(
'different account',
);
});
it('fails clearly on corrupt records and symlinks', async () => {
const dir = await directory();
const tasks = await openTasks(dir);
await tasks.close();
const file = path.join(dir, `${randomUUID()}.task.json`);
await writeFile(file, '{broken', { mode: 0o600 });
await expect(PersistentTasks.open(dir, { scopeId })).rejects.toThrow();
await rm(file);
await symlink(path.join(dir, 'scope.json'), file);
await expect(PersistentTasks.open(dir, { scopeId })).rejects.toThrow(
'private regular file',
);
});
it('rejects traversal and unknown identifiers without touching other files', async () => {
const tasks = await openTasks();
expect(() => tasks.get('../scope')).toThrow('Invalid task identifier');
expect(() => tasks.get(randomUUID())).toThrow('Unknown task identifier');
await expect(tasks.cancel('../scope')).rejects.toThrow('Invalid task identifier');
});
it.skipIf(process.platform === 'win32')('rejects group-readable directories', async () => {
const dir = await directory();
await chmod(dir, 0o755);
await expect(PersistentTasks.open(dir, { scopeId })).rejects.toThrow('0700');
});
it('reports notification failures without changing a completed result', async () => {
const onError = vi.fn();
const tasks = await openTasks(undefined, {
onError,
onChange: () => {
throw new Error('subscriber unavailable');
},
});
const handle = await tasks.start(async () => success);
expect((await finished(tasks, handle.taskId)).status).toBe('completed');
expect(onError).toHaveBeenCalledWith(
expect.any(Error),
expect.objectContaining({ event: 'task_notification_failed' }),
);
});
it('does not let a stalled subscriber block task execution or cancellation', async () => {
const tasks = await openTasks(undefined, {
onChange: () => new Promise<void>(() => undefined),
});
const handle = await tasks.start(async ({ signal }) => {
await new Promise((_, reject) =>
signal.addEventListener('abort', () => reject(signal.reason), { once: true }),
);
return success;
});
await tasks.cancel(handle.taskId);
expect((await finished(tasks, handle.taskId)).status).toBe('cancelled');
});
});
describe('task cancellation reaches HTTP and upload cleanup', () => {
it.each([false, true])(
'aborts the active HTTP operation and finishes cleanup (upload: %s)',
async (upload) => {
const dir = await directory();
const inputFile = path.join(dir, 'media.bin');
await writeFile(inputFile, 'media');
const tasks = await openTasks();
const signals: AbortSignal[] = [];
const reset = vi.fn();
const client = makeHttpClient({
endpoint: 'https://unused.invalid',
streamDir: dir,
uploadRoot: dir,
fetch: async (_url, init) => {
const body = JSON.parse(String(init?.body));
if (body.params.reset) {
reset();
expect(init?.signal?.aborted).toBe(false);
return new Response(
`${JSON.stringify({ status: 'ok', retcode: 0, data: { type: 'response' } })}\r\n\r\n`,
);
}
const signal = init?.signal;
if (!signal) {
throw new Error('Expected cancellation signal');
}
signals.push(signal);
return new Promise<Response>((_resolve, reject) => {
signal.addEventListener('abort', () => reject(signal.reason), {
once: true,
});
});
},
});
const handle = await tasks.start(({ signal }) =>
callTool(
upload ? 'invoke_action' : 'query_action',
upload
? { action: 'upload_file_stream', execution: { input_file: inputFile } }
: { action: 'get_status' },
{ mode: 'write', client, signal },
),
);
await vi.waitFor(() => expect(signals).toHaveLength(1));
await tasks.cancel(handle.taskId);
const result = await finished(tasks, handle.taskId);
expect(signals[0].aborted).toBe(true);
expect(result).toMatchObject({
status: 'completed',
result: {
isError: true,
structuredContent: { diagnostics: { kind: 'cancelled', outcome: 'unknown' } },
},
});
expect(reset).toHaveBeenCalledTimes(upload ? 1 : 0);
},
);
});
describe('2026 Tasks wire handlers', () => {
it('does not activate modern tasks on a legacy connection with copied capability metadata', async () => {
const tasks = await openTasks();
const task = await tasks.start(async () => success);
const client: ActionClient = {
call: vi.fn(async () => ({ status: 'ok', retcode: 0 })),
callStream: vi.fn(async () => ({
frame_count: 0,
frames: [],
terminal: { status: 'ok', retcode: 0 },
})),
};
const server = createMcpServer({ mode: 'write', tasks, client });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
cleanup.push(() => server.close());
await server.connect(serverTransport);
await clientTransport.start();
let id = 0;
const request = async (method: string, params: Record<string, unknown> = {}) => {
const response = new Promise<unknown>((resolve) => {
clientTransport.onmessage = resolve;
});
await clientTransport.send({
jsonrpc: '2.0',
id: ++id,
method,
params: {
...params,
_meta: {
'io.modelcontextprotocol/clientCapabilities': {
extensions: { [TASK_EXTENSION]: {} },
},
},
},
});
return response;
};
expect(await request('tools/list')).toMatchObject({
result: {
tools: expect.not.arrayContaining([
expect.objectContaining({ name: 'invoke_action_background' }),
]),
},
});
expect(
await request('tools/call', {
name: 'invoke_action_background',
arguments: { action: 'test_download_stream' },
}),
).toMatchObject({
result: {
isError: true,
structuredContent: { diagnostics: { kind: 'configuration' } },
},
});
expect(client.call).not.toHaveBeenCalled();
expect(client.callStream).not.toHaveBeenCalled();
expect(tasks.list().tasks).toHaveLength(1);
expect(await request('tasks/get', { taskId: task.taskId })).toMatchObject({
error: { code: -32021 },
});
});
async function harness() {
const tasks = await openTasks();
const handler = createMcpHandler(() => {
const server = new Server(
{ name: 'task-test', version: '1' },
{ capabilities: { tools: {} } },
);
installTaskHandlers(server, tasks);
server.setRequestHandler('tools/call', (request, ctx) => {
expect(supportsTasks(ctx.mcpReq.envelope)).toBe(true);
return tasks.start(async ({ requestInput }) => {
if (request.params.name === 'input') {
await requestInput({ answer: input });
}
return success;
});
});
return server;
});
cleanup.push(() => handler.close());
const request = async (
method: string,
params: Record<string, unknown> = {},
capable = true,
) => {
const response = await handler.fetch(
new Request('http://mcp.invalid/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json, text/event-stream',
'Mcp-Method': method,
'Mcp-Protocol-Version': '2026-07-28',
...(typeof params.name === 'string'
? { 'Mcp-Name': params.name }
: typeof params.taskId === 'string'
? { 'Mcp-Name': params.taskId }
: {}),
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method,
params: {
...params,
_meta: {
'io.modelcontextprotocol/protocolVersion': '2026-07-28',
'io.modelcontextprotocol/clientCapabilities': capable
? { extensions: { [TASK_EXTENSION]: {} } }
: {},
},
},
}),
}),
);
return response.json();
};
return { tasks, request };
}
it('creates a real task response and fetches durable terminal results', async () => {
const { tasks, request } = await harness();
const created = await request('tools/call', { name: 'work' });
expect(created.result).toMatchObject({ resultType: 'task', status: 'working' });
await finished(tasks, created.result.taskId);
expect(
(await request('tasks/get', { taskId: created.result.taskId })).result,
).toMatchObject({
resultType: 'complete',
status: 'completed',
result: { ...success, resultType: 'complete' },
});
expect(
(await request('tasks/cancel', { taskId: created.result.taskId })).result,
).toMatchObject({ resultType: 'complete' });
});
it('restores SDK-lifted input responses and acknowledges updates', async () => {
const { tasks, request } = await harness();
const created = await request('tools/call', { name: 'input' });
const taskId = created.result.taskId;
await vi.waitFor(() => expect(tasks.get(taskId).status).toBe('input_required'));
expect(
(
await request('tasks/update', {
taskId,
inputResponses: { irrelevant: { invalid: true } },
})
).result,
).toMatchObject({ resultType: 'complete' });
expect(
(
await request('tasks/update', {
taskId,
inputResponses: { answer: { invalid: true } },
})
).error.code,
).toBe(-32602);
expect(
(
await request('tasks/update', {
taskId,
inputResponses: { answer: { action: 'accept', content: { value: 'yes' } } },
})
).result,
).toMatchObject({ resultType: 'complete' });
expect((await finished(tasks, taskId)).status).toBe('completed');
});
it('checks the capability on every lifecycle call, and does not expose obsolete methods', async () => {
const { request } = await harness();
const created = await request('tools/call', { name: 'work' });
const taskId = created.result.taskId;
for (const method of ['tasks/get', 'tasks/update', 'tasks/cancel']) {
expect((await request(method, { taskId }, false)).error).toMatchObject({
code: -32021,
data: { requiredCapabilities: { extensions: { [TASK_EXTENSION]: {} } } },
});
}
expect((await request('tasks/get', { taskId: randomUUID() })).error.code).toBe(-32602);
expect((await request('tasks/list')).error).toBeDefined();
expect((await request('tasks/result', { taskId })).error).toBeDefined();
});
});