mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
213 lines
11 KiB
JavaScript
213 lines
11 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFileSync, spawn, spawnSync } from 'node:child_process';
|
|
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { basename, join } from 'node:path';
|
|
import test from 'node:test';
|
|
import { createPiAdapter } from '../../src/adapters/pi.mjs';
|
|
import { createRuntimeAdapterRegistry } from '../../src/adapters/registry.mjs';
|
|
import { profileCapabilityIssues } from '../../src/adapters/runtime.mjs';
|
|
import { describeTorchTools, TORCH_MCP_TOOL_NAMES } from '../../src/mcp/tools.mjs';
|
|
import { createTorchPiExtension } from '../../src/adapters/pi/extension.mjs';
|
|
import { analyzeRepository } from '../../src/kernel/analyze.mjs';
|
|
import { proposeDomains } from '../../src/kernel/domains.mjs';
|
|
import { inspectRepository } from '../../src/kernel/git.mjs';
|
|
import { installProject } from '../../src/kernel/install.mjs';
|
|
import { createWorktrees } from '../../src/kernel/worktrees.mjs';
|
|
import { openControlPlane } from '../../src/control-plane/service.mjs';
|
|
import { planFleetUp } from '../../src/runtime/lifecycle.mjs';
|
|
|
|
const MCP_SERVER = new URL('../../src/mcp/server.mjs', import.meta.url).pathname;
|
|
|
|
function fixture({ withWorktrees = false } = {}) {
|
|
const root = mkdtempSync(join(tmpdir(), 'torch-pi-'));
|
|
const env = { ...process.env, XDG_DATA_HOME: join(tmpdir(), `${basename(root)}-data`) };
|
|
execFileSync('git', ['init', '-b', 'main', root]);
|
|
execFileSync('git', ['-C', root, 'config', 'user.email', '[email protected]']);
|
|
execFileSync('git', ['-C', root, 'config', 'user.name', 'TORCH Test']);
|
|
writeFileSync(join(root, 'app.js'), 'export const app = true;\n');
|
|
execFileSync('git', ['-C', root, 'add', '.']);
|
|
execFileSync('git', ['-C', root, 'commit', '-m', 'fixture']);
|
|
const repository = inspectRepository(root);
|
|
const proposal = proposeDomains({ repository, analysis: analyzeRepository(repository) });
|
|
const worker = proposal.domains[0];
|
|
worker.runtime = 'pi';
|
|
worker.model = 'openai/test-model';
|
|
worker.reasoning = 'high';
|
|
proposal.review = {
|
|
status: 'approved', reviewedAt: '2026-09-28T00:00:00Z', reviewedBy: 'fixture-owner', notes: [],
|
|
};
|
|
installProject({ repository, proposal, env, projectId: 'pi-fixture', runtimes: ['claude', 'codex', 'pi'] });
|
|
if (withWorktrees) {
|
|
execFileSync('git', ['-C', root, 'add', '.torch']);
|
|
execFileSync('git', ['-C', root, 'commit', '-m', 'install TORCH fixture']);
|
|
createWorktrees({
|
|
repository: inspectRepository(root),
|
|
parentOverride: join(tmpdir(), `${basename(root)}-worktrees`),
|
|
});
|
|
}
|
|
return { root, env, worker: worker.id };
|
|
}
|
|
|
|
test('SCN-pi-adapter: Pi profiles produce isolated resumable-turn plans with an explicit required model', () => {
|
|
const adapter = createPiAdapter({ idFactory: () => 'pi-session-123' });
|
|
const integration = adapter.configure({
|
|
repositoryRoot: '/project', areaId: 'worker', mcpEntry: '/torch/src/mcp/server.mjs',
|
|
stateRoot: '/private/torch/project-state',
|
|
});
|
|
assert.equal(integration.mutationPerformed, false);
|
|
assert.equal(integration.integration.type, 'pi-mcp-extension');
|
|
assert.equal(integration.integration.sessionDir, '/private/torch/project-state/sessions/pi/worker');
|
|
|
|
const start = adapter.createSession({
|
|
areaId: 'worker', title: 'Worker', worktree: '/worktree', promptFile: '/private/prompts/worker.md',
|
|
firstMessage: 'Begin.', model: 'openai/test-model', reasoning: 'high', integration: integration.integration,
|
|
});
|
|
assert.equal(start.runtimeSessionId, 'pi-session-123');
|
|
assert.equal(start.requiresRuntimeIdCapture, false);
|
|
assert.equal(start.completionState, 'idle');
|
|
assert.equal(start.launch.command, 'pi');
|
|
assert.equal(start.launch.cwd, '/worktree');
|
|
assert.equal(start.launch.args.includes('--no-extensions'), true);
|
|
assert.equal(start.launch.args.includes('--no-context-files'), true);
|
|
assert.equal(start.launch.args.includes('--no-approve'), true);
|
|
assert.equal(start.launch.args.includes('/private/torch/project-state/sessions/pi/worker'), true);
|
|
assert.equal(start.launch.args.includes('openai/test-model'), true);
|
|
assert.deepEqual(start.launch.args.slice(start.launch.args.indexOf('--thinking'), start.launch.args.indexOf('--thinking') + 2), ['--thinking', 'high']);
|
|
|
|
const resumed = adapter.resumeSession({
|
|
areaId: 'worker', runtimeSessionId: start.runtimeSessionId, worktree: '/worktree',
|
|
promptFile: '/private/prompts/worker-current.md',
|
|
message: 'Continue from durable TORCH state.', model: 'openai/test-model', reasoning: 'medium',
|
|
integration: integration.integration,
|
|
});
|
|
assert.equal(resumed.runtimeSessionId, start.runtimeSessionId);
|
|
assert.equal(resumed.launch.args.includes('--session-id'), true);
|
|
assert.equal(resumed.launch.args.includes('Continue from durable TORCH state.'), true);
|
|
assert.deepEqual(resumed.launch.args.slice(resumed.launch.args.indexOf('--append-system-prompt'), resumed.launch.args.indexOf('--append-system-prompt') + 2), [
|
|
'--append-system-prompt', '/private/prompts/worker-current.md',
|
|
]);
|
|
assert.throws(() => adapter.createSession({
|
|
areaId: 'worker', worktree: '/worktree', promptFile: '/prompt', firstMessage: 'Begin.',
|
|
model: 'openai/test-model', integration: {},
|
|
}), (error) => error.code === 'RUNTIME_CONFIGURATION_INVALID');
|
|
assert.throws(() => adapter.createSession({
|
|
areaId: 'worker', worktree: '/worktree', promptFile: '/prompt', firstMessage: 'Begin.',
|
|
model: 'openai/test-model', reasoning: 'ultra', integration: integration.integration,
|
|
}), (error) => error.code === 'RUNTIME_PROFILE_INVALID');
|
|
assert.deepEqual(adapter.capabilities.processModel, 'resumable-turn');
|
|
assert.throws(() => adapter.stopSession(), (error) => error.code === 'RUNTIME_CAPABILITY_UNSUPPORTED');
|
|
assert.equal(createRuntimeAdapterRegistry().has('pi'), true);
|
|
assert.deepEqual(adapter.capabilities.modelRequired, true);
|
|
assert.deepEqual(profileCapabilityIssues(adapter, { model: null, reasoning: 'high' }), [
|
|
{ field: 'model', capability: 'modelSelection', required: true },
|
|
]);
|
|
});
|
|
|
|
test('SCN-pi-lifecycle-plan: installed fleet planning passes identity-bound Pi integration without starting it', () => {
|
|
const context = fixture({ withWorktrees: true });
|
|
const control = openControlPlane({ repositoryRoot: context.root, env: context.env });
|
|
try {
|
|
const registry = createRuntimeAdapterRegistry({ env: context.env });
|
|
const plan = planFleetUp({
|
|
repositoryRoot: context.root, controlPlane: control, adapters: registry.toMap(), fresh: true,
|
|
});
|
|
assert.equal(plan.canProceed, true, JSON.stringify(plan.blockers));
|
|
const action = plan.actions.find((candidate) => candidate.areaId === context.worker);
|
|
assert.equal(action.runtime, 'pi');
|
|
assert.equal(action.profile.model, 'openai/test-model');
|
|
assert.equal(action.profile.reasoning, 'high');
|
|
assert.equal(action.integration.type, 'pi-mcp-extension');
|
|
assert.equal(action.integration.areaId, context.worker);
|
|
assert.equal(action.integration.repositoryRoot, context.root);
|
|
assert.equal(action.launch.args.includes(`--torch-area`), true);
|
|
assert.equal(action.launch.args.includes(context.worker), true);
|
|
assert.equal(action.launch.args.includes('openai/test-model'), true);
|
|
assert.equal(action.requiresRuntimeIdCapture, false);
|
|
assert.equal(action.completionState, 'idle');
|
|
assert.equal(plan.mutationPerformed, false);
|
|
assert.equal(control.identity(context.worker).runtimeSessionId, null);
|
|
} finally {
|
|
control.close();
|
|
}
|
|
});
|
|
|
|
test('SCN-pi-mcp-bridge: Pi exposes every identity-bound TORCH MCP tool without project extension discovery', async () => {
|
|
const context = fixture();
|
|
const tools = [];
|
|
const flags = new Map([
|
|
['torch-root', context.root], ['torch-area', context.worker],
|
|
['torch-mcp-entry', MCP_SERVER], ['torch-node', process.execPath],
|
|
]);
|
|
const pi = {
|
|
registerFlag: (name, options) => flags.set(`registered:${name}`, options),
|
|
getFlag: (name) => flags.get(name),
|
|
registerTool: (tool) => tools.push(tool),
|
|
};
|
|
const result = createTorchPiExtension(pi, {
|
|
Type: { Unsafe: (schema) => schema },
|
|
spawnProcess: (command, args, options) => spawn(command, args, { ...options, env: context.env }),
|
|
});
|
|
assert.deepEqual(result.registered, TORCH_MCP_TOOL_NAMES);
|
|
assert.deepEqual(tools.map((tool) => tool.name), TORCH_MCP_TOOL_NAMES);
|
|
assert.equal(describeTorchTools().length, TORCH_MCP_TOOL_NAMES.length);
|
|
assert.equal(flags.get('registered:torch-area').type, 'string');
|
|
|
|
const identityTool = tools.find((tool) => tool.name === 'torch_identity');
|
|
const identityResult = await identityTool.execute('call-1', {}, undefined, undefined, {});
|
|
const identity = JSON.parse(identityResult.content[0].text);
|
|
assert.equal(identity.areaId, context.worker);
|
|
assert.equal(identity.runtime, 'pi');
|
|
|
|
await assert.rejects(
|
|
identityTool.execute('call-2', { area_id: 'session-manager' }, undefined, undefined, {}),
|
|
/cannot act as session-manager/i,
|
|
);
|
|
const messageTool = tools.find((tool) => tool.name === 'torch_send_message');
|
|
const messageResult = await messageTool.execute('call-3', {
|
|
recipient: 'session-manager', body: 'Pi MCP bridge verified.',
|
|
}, undefined, undefined, {});
|
|
assert.match(messageResult.content[0].text, /message/i);
|
|
});
|
|
|
|
test('SCN-pi-installed-extension-startup: an installed Pi loads the bundled bridge offline without a provider turn', (t) => {
|
|
const version = spawnSync('pi', ['--version'], { encoding: 'utf8', timeout: 5_000 });
|
|
if (version.error?.code === 'ENOENT') {
|
|
t.skip('Pi CLI is not installed in this environment');
|
|
return;
|
|
}
|
|
assert.equal(version.status, 0, version.stderr);
|
|
|
|
const context = fixture();
|
|
const privateConfig = join(context.root, '.pi-smoke-config');
|
|
const privateSessions = join(context.root, '.pi-smoke-sessions');
|
|
const result = spawnSync('pi', [
|
|
'--no-session', '--mode', 'rpc', '--no-extensions', '--no-context-files', '--no-approve', '--offline',
|
|
'--extension', new URL('../../src/adapters/pi/extension.mjs', import.meta.url).pathname,
|
|
'--torch-root', context.root,
|
|
'--torch-area', context.worker,
|
|
'--torch-mcp-entry', MCP_SERVER,
|
|
'--torch-node', process.execPath,
|
|
'--session-dir', privateSessions,
|
|
], {
|
|
cwd: context.root,
|
|
env: {
|
|
...context.env,
|
|
PI_OFFLINE: '1',
|
|
PI_TELEMETRY: '0',
|
|
PI_CODING_AGENT_DIR: privateConfig,
|
|
PI_CODING_AGENT_SESSION_DIR: privateSessions,
|
|
},
|
|
input: '{"type":"get_state","id":"torch-extension-smoke"}\n',
|
|
encoding: 'utf8',
|
|
timeout: 15_000,
|
|
});
|
|
|
|
assert.equal(result.error, undefined, result.error?.message);
|
|
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
const events = result.stdout.trim().split('\n').map((line) => JSON.parse(line));
|
|
assert.equal(events.some((event) => event.type === 'extension_error'), false, result.stdout);
|
|
assert.equal(events.some((event) => event.type === 'response'
|
|
&& event.id === 'torch-extension-smoke' && event.command === 'get_state' && event.success), true, result.stdout);
|
|
});
|