blob: dcdec8e12cee78a9812532aa5622776ba722f0e4 [file]
import assert from 'node:assert/strict';
import { access, mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, before, describe, test } from 'node:test';
import type { SessionEvent } from '@maka/core';
import type { SessionSummary } from '@maka/core';
import {
decodeActivationRequest,
parseMakaActivateArgs,
runMakaActivationCli,
type MakaActivationContext,
type MakaActivationDeps,
type MakaActivationRuntime,
} from '../activation-command.js';
import { parseMakaCliArgs } from '../cli.js';
import type { MakaRunOutcome } from '../run-command-core.js';
const ROOTS = {
stateRoot: '/tmp/maka-state',
workspaceRoot: '/tmp/maka-workspace',
configRoot: '/tmp/maka-config',
};
before(async () => {
await Promise.all(Object.values(ROOTS).map((root) => mkdir(root, { recursive: true })));
});
after(async () => {
await Promise.all(Object.values(ROOTS).map((root) => rm(root, { recursive: true, force: true })));
});
function validRequest(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return {
schemaVersion: 1,
activationId: 'activation-1',
cloudSessionId: 'cloud-session-1',
stimulus: { type: 'message', payload: { text: 'Inspect the workspace' } },
...overrides,
};
}
function summary(overrides: Partial<SessionSummary> = {}): SessionSummary {
return {
id: 'maka-session-1',
cwd: ROOTS.workspaceRoot,
name: 'activation',
isFlagged: false,
isArchived: false,
labels: [],
hasUnread: false,
status: 'active',
backend: 'ai-sdk',
llmConnectionSlug: 'local',
connectionLocked: true,
model: 'fixture-model',
permissionMode: 'explore',
...overrides,
};
}
function completedResult(): MakaRunOutcome {
return {
outcomeId: 'run-1',
status: 'completed',
finalOutput: 'done sk-test-secret',
sandboxBoundary: 'none',
};
}
function completedEvents(): SessionEvent[] {
return [
{
type: 'text_delta',
id: 'event-1',
turnId: 'turn-1',
ts: 1,
messageId: 'message-1',
text: 'done',
},
];
}
function fakeDeps(
options: {
input?: string;
sessions?: SessionSummary[];
result?: MakaRunOutcome;
events?: SessionEvent[];
onContext?: (input: Parameters<NonNullable<MakaActivationDeps['createContext']>>[0]) => void;
onCreateSession?: () => void;
onClose?: () => void;
safeBoundaryResume?: boolean;
onResume?: () => void;
onSandboxBoundaryResponse?: (response: { requestId: string; decision: 'deny' }) => void;
sendMessage?: (
runtime: MakaActivationRuntime,
sessionId: string,
input: { turnId: string; text: string },
) => AsyncIterable<SessionEvent>;
} = {},
): MakaActivationDeps {
let observer: ((result: MakaRunOutcome) => void | Promise<void>) | undefined;
const sessions = options.sessions ?? [];
const runtime: MakaActivationRuntime = {
async createSession() {
options.onCreateSession?.();
return summary();
},
listSessions: async () => sessions,
...(options.safeBoundaryResume
? {
resumeLatest: async () =>
(async function* () {
options.onResume?.();
for (const event of options.events ?? completedEvents()) yield event;
await observer?.(options.result ?? completedResult());
})(),
}
: {}),
async *sendMessage(sessionId, input) {
if (options.sendMessage) {
yield* options.sendMessage(runtime, sessionId, input);
} else {
for (const event of options.events ?? completedEvents()) yield event;
}
await observer?.(options.result ?? completedResult());
},
async respondToSandboxBoundary(_sessionId, response) {
options.onSandboxBoundaryResponse?.(response);
},
async stopSession() {},
};
return {
createContext: async (input) => {
options.onContext?.(input);
observer = input.runOutcomeObserver;
const context: MakaActivationContext = {
runtime,
target: {
connection: {
slug: 'local',
name: 'Fixture',
providerType: 'ollama',
enabled: true,
defaultModel: 'fixture-model',
},
apiKey: '',
model: 'fixture-model',
},
close: async () => options.onClose?.(),
};
return context;
},
listSessions: async () => sessions,
workspaceRoot: () => ROOTS.workspaceRoot,
processCwd: () => ROOTS.workspaceRoot,
stdinIsTTY: () => false,
readStdin: async () => options.input ?? JSON.stringify(validRequest()),
readFile: async () => options.input ?? JSON.stringify(validRequest()),
writeStdout: () => {},
writeStderr: () => {},
onSigint: () => () => {},
setTimer: (handler, ms) => setTimeout(handler, ms),
clearTimer: (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>),
newId: () => 'turn-1',
};
}
describe('maka activate argument and request contracts', () => {
test('routes activate through the CLI parser', () => {
assert.deepEqual(parseMakaCliArgs(['activate'], '0.1.0'), {
kind: 'activate',
args: [],
});
});
test('requires explicit non-overlapping roots and accepts JSON input options', () => {
assert.deepEqual(
parseMakaActivateArgs([
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
'--input',
'-',
'--timeout',
'2.5',
'--max-steps',
'4',
'--permission-mode',
'bypass',
]),
{
kind: 'activate',
options: {
...ROOTS,
input: '-',
timeoutMs: 2500,
maxSteps: 4,
permissionMode: 'bypass',
},
},
);
assert.equal(parseMakaActivateArgs(['--state-root', ROOTS.stateRoot]).kind, 'error');
assert.equal(
parseMakaActivateArgs([
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
'prompt-in-argv',
]).kind,
'error',
);
});
test('decodes versioned message and resumed activation requests', () => {
assert.deepEqual(
decodeActivationRequest(
validRequest({
makaSessionId: 'maka-session-1',
stimulus: { type: 'schedule', payload: { job: 'nightly' } },
}),
),
{
schemaVersion: 1,
activationId: 'activation-1',
cloudSessionId: 'cloud-session-1',
makaSessionId: 'maka-session-1',
stimulus: { type: 'schedule', payload: { job: 'nightly' } },
},
);
assert.throws(
() => decodeActivationRequest(validRequest({ schemaVersion: 2 })),
/unsupported activation schema/,
);
assert.throws(
() => decodeActivationRequest({ ...validRequest(), unexpected: true }),
/unsupported activation field/,
);
});
test('rejects malformed input before runtime startup', async () => {
let started = false;
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({ input: '{"schemaVersion":2}' }),
createContext: async () => {
started = true;
throw new Error('must not start');
},
},
);
assert.equal(result, 2);
assert.equal(started, false);
});
});
describe('maka activate JSONL protocol', () => {
test('emits start, redacted runtime events, and exactly one completed outcome', async () => {
const lines: string[] = [];
let closed = false;
let terminalSawClosed = false;
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({
onClose: () => {
closed = true;
},
}),
writeStdout: (text) => {
lines.push(text.trim());
if (text.includes('"type":"outcome"')) terminalSawClosed = closed;
},
},
);
assert.equal(result, 0);
assert.equal(terminalSawClosed, true);
assert.equal(lines.length, 3);
assert.equal(JSON.parse(lines[0]!).type, 'start');
assert.equal(JSON.parse(lines[1]!).type, 'runtime_event');
assert.equal(JSON.parse(lines[2]!).type, 'outcome');
assert.equal(JSON.parse(lines[2]!).status, 'completed');
assert.equal(lines.join('\n').includes('sk-test-secret'), false);
});
test('emits a terminal fatal outcome for malformed JSON input', async () => {
const lines: string[] = [];
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({ input: '{"schemaVersion": 1' }),
newId: () => 'generated-id',
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 2);
assert.deepEqual(
lines.map((line) => JSON.parse(line).type),
['start', 'outcome'],
);
assert.deepEqual(JSON.parse(lines.at(-1)!), {
protocol: 'maka.activation',
schemaVersion: 1,
type: 'outcome',
activationId: 'generated-id',
cloudSessionId: 'unknown',
status: 'fatal_failure',
reason: 'malformed_input',
});
});
test('returns blocked after denying a non-interactive sandbox boundary request', async () => {
const lines: string[] = [];
const responses: Array<{ requestId: string; decision: 'deny' }> = [];
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({
result: {
...completedResult(),
status: 'failed',
finalOutput: undefined,
failure: { class: 'permission_denied' },
sandboxBoundary: 'unresolved',
},
onSandboxBoundaryResponse: (response) => responses.push(response),
events: [
{
type: 'sandbox_boundary_request',
id: 'boundary-event',
turnId: 'turn-1',
ts: 1,
requestId: 'boundary-1',
toolUseId: 'tool-1',
justification: 'write generated output',
expansion: {
filesystem: {
entries: [{ path: '/tmp/output', access: 'write', scope: 'subtree' }],
},
},
},
],
}),
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 3);
assert.deepEqual(responses, [{ requestId: 'boundary-1', decision: 'deny' }]);
assert.equal(JSON.parse(lines.at(-1)!).status, 'blocked');
assert.equal(JSON.parse(lines.at(-1)!).reason, 'permission_denied');
});
test('returns blocked for an unresolved production sandbox tool failure', async () => {
const lines: string[] = [];
const boundaryFailure = {
kind: 'text',
text: 'Write requires an approved session sandbox boundary expansion.',
sandboxFailure: {
reason: 'sandbox_boundary_required',
requiredExpansion: {
filesystem: {
entries: [{ path: '/tmp/output', access: 'write', scope: 'subtree' }],
},
},
},
} as const;
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({
result: {
...completedResult(),
finalOutput: 'continued after the failed write',
sandboxBoundary: 'unresolved',
},
events: [
{
type: 'tool_result',
id: 'event-boundary-result',
turnId: 'turn-1',
ts: 1,
toolUseId: 'tool-boundary',
isError: true,
content: boundaryFailure,
},
],
}),
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 3);
assert.deepEqual(JSON.parse(lines.at(-1)!), {
protocol: 'maka.activation',
schemaVersion: 1,
type: 'outcome',
activationId: 'activation-1',
cloudSessionId: 'cloud-session-1',
makaSessionId: 'maka-session-1',
status: 'blocked',
reason: 'permission_required',
requiredAction: 'grant_permission',
});
});
test('retries non-permission blocked sessions instead of requesting permission', async () => {
for (const blockedReason of ['auth', 'tool_failed'] as const) {
const lines: string[] = [];
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({
input: JSON.stringify(validRequest({ makaSessionId: 'maka-session-1' })),
sessions: [summary({ status: 'blocked', blockedReason })],
}),
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 4);
assert.deepEqual(JSON.parse(lines.at(-1)!), {
protocol: 'maka.activation',
schemaVersion: 1,
type: 'outcome',
activationId: 'activation-1',
cloudSessionId: 'cloud-session-1',
makaSessionId: 'maka-session-1',
status: 'retryable_failure',
reason: blockedReason,
requiredAction: 'retry_activation',
});
}
});
test('resumes an existing compatible session without creating another one', async () => {
let created = false;
let resumed = false;
let requestedConnection: string | undefined;
const lines: string[] = [];
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
],
{
...fakeDeps({
input: JSON.stringify(validRequest({ makaSessionId: 'maka-session-1' })),
sessions: [summary()],
onCreateSession: () => {
created = true;
},
onContext: (input) => {
requestedConnection = input.requestedConnectionSlug;
},
safeBoundaryResume: true,
onResume: () => {
resumed = true;
},
}),
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 0);
assert.equal(created, false);
assert.equal(resumed, true);
assert.equal(requestedConnection, 'local');
assert.equal(JSON.parse(lines.at(-1)!).makaSessionId, 'maka-session-1');
});
test('maps timeout to retryable_failure and emits one terminal outcome', async () => {
const lines: string[] = [];
let release: (() => void) | undefined;
const result = await runMakaActivationCli(
[
'--state-root',
ROOTS.stateRoot,
'--workspace-root',
ROOTS.workspaceRoot,
'--config-root',
ROOTS.configRoot,
'--timeout',
'0.01',
],
{
...fakeDeps({
sendMessage: async function* () {
await new Promise<void>((resolve) => {
release = resolve;
});
},
}),
writeStdout: (text) => lines.push(text.trim()),
},
);
release?.();
assert.equal(result, 4);
const outcomes = lines.filter((line) => JSON.parse(line).type === 'outcome');
assert.equal(outcomes.length, 1);
assert.deepEqual(JSON.parse(outcomes[0]!), {
protocol: 'maka.activation',
schemaVersion: 1,
type: 'outcome',
activationId: 'activation-1',
cloudSessionId: 'cloud-session-1',
makaSessionId: 'maka-session-1',
status: 'retryable_failure',
reason: 'timeout',
});
});
});
describe('maka activate filesystem boundaries', () => {
test('rejects workspace/state/config overlap before bootstrap', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-activation-'));
try {
await mkdir(join(root, 'state'));
await mkdir(join(root, 'workspace'));
const lines: string[] = [];
const result = await runMakaActivationCli(
[
'--state-root',
join(root, 'state'),
'--workspace-root',
root,
'--config-root',
join(root, 'config'),
],
{
...fakeDeps(),
writeStdout: (text) => lines.push(text.trim()),
},
);
assert.equal(result, 2);
assert.equal(JSON.parse(lines.at(-1)!).reason, 'unsafe_roots');
await assert.rejects(access(join(root, 'config')), { code: 'ENOENT' });
const reverseLines: string[] = [];
const reverseResult = await runMakaActivationCli(
[
'--state-root',
join(root, 'state'),
'--workspace-root',
join(root, 'workspace'),
'--config-root',
root,
],
{
...fakeDeps(),
writeStdout: (text) => reverseLines.push(text.trim()),
},
);
assert.equal(reverseResult, 2);
assert.equal(JSON.parse(reverseLines.at(-1)!).reason, 'unsafe_roots');
} finally {
await rm(root, { recursive: true, force: true });
}
});
});