blob: dae328a053d22ce7a20e68623bd5839095bdebe1 [file]
import assert from 'node:assert/strict';
import { fork, type ChildProcess } from 'node:child_process';
import { randomUUID } from 'node:crypto';
import {
appendFile,
chmod,
mkdir,
mkdtemp,
readFile,
readdir,
rm,
writeFile,
} from 'node:fs/promises';
import { createServer, type Server } from 'node:http';
import { connect, type Socket } from 'node:net';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { test } from 'node:test';
import { TOOL_BOUNDARY_PROTOCOL_V1 } from '@maka/core';
import { canonicalToolArgsHash } from '@maka/core';
import type { AgentRunHeader } from '@maka/core';
import type { MessageContent } from '@maka/core';
import type { ConnectionCatalogEntry } from '@maka/core';
import type { StoredMessage } from '@maka/core';
import type { Task } from '@maka/core';
import { isTerminalRuntimeEvent } from '@maka/core';
import type { RuntimeEvent } from '@maka/core';
import {
buildTaskLedgerTools,
buildRecoveredTerminalRuntimeEvent,
classifyTerminalRuntimeLedger,
commitTerminalRunWithRuntimeFact,
FAKE_ASK_USER_QUESTION_PROMPT,
FAKE_WAIT_FOR_STEERING_PROMPT,
type MakaTool,
type MakaToolContext,
} from '@maka/runtime';
import {
openInteractiveExecutionStoresForRead,
openInteractiveExecutionStoresForWrite,
} from '@maka/storage/execution-stores';
import { openInteractiveRuntimePolicyStoresForWrite } from '@maka/storage/runtime-policy-stores';
import {
resolveRootControlNamespace,
resolveStorageRoot,
tryAcquireInteractiveRootOwner,
tryAcquireInteractiveRootReader,
type StorageRootCapability,
} from '@maka/storage/root-authority';
import { openInteractiveTaskLedgerStoreForWrite } from '@maka/storage/task-ledger-authority';
import {
connectRuntimeHost,
RuntimeHostOperationError,
RuntimeHostSubscriptionError,
type RuntimeHostConnection,
type RuntimeHostSessionSubscription,
} from '../client/index.js';
import {
decodeHostFrame,
RUNTIME_HOST_PROTOCOL_VERSION,
TASK_LEDGER_PAGE_MAX_ITEMS,
type ConnectionCatalogQueryResult,
type InteractionPendingSnapshot,
type SubscriptionFrame,
type TaskLedgerQueryResult,
type TaskLedgerRevision,
type TurnMessageSubmitInput,
type TurnSnapshot,
type TurnStartResult,
} from '../protocol/index.js';
import { SessionAdmissionGate } from '../server/session-admission-gate.js';
import { HostTaskLedgerCoordinator } from '../server/task-ledger-coordinator.js';
import { FramedTransport } from '../transport/framed-transport.js';
import {
CONNECTION_EFFECT_MODEL_IDS,
PROCESS_TIMEOUT_MS,
SubscriptionProbe,
assertJsonLines,
attachment,
connectClient,
requireStartedTurn,
operationError,
quotedContent,
sendStartWithoutReadingResponse,
startConnectionEffectProvider,
userRuntimeContent,
waitForDurableMessageConflict,
waitForPendingInteraction,
waitForRunningTurn,
waitForTerminalTurn,
waitForTurn,
withExecutionRoot,
withTimeout,
} from './fixtures/execution-host-suite.js';
test('subscribed Clients share one canonical queue and ordered root handoff', async () => {
await withExecutionRoot(async (fixture) => {
const host = await fixture.startHost();
const desktop = await connectClient(fixture.root, 'desktop');
const tui = await connectClient(fixture.root, 'tui');
const desktopSubscription = await desktop.openSessionSubscription({
sessionId: fixture.sessionId,
});
const tuiSubscription = await tui.openSessionSubscription({ sessionId: fixture.sessionId });
const desktopProbe = new SubscriptionProbe(desktopSubscription);
const tuiProbe = new SubscriptionProbe(tuiSubscription);
for (const subscription of [desktopSubscription, tuiSubscription]) {
assert.equal(subscription.hostEpoch, host.hostEpoch);
assert.equal(subscription.snapshot.rootTurn, null);
assert.equal(subscription.snapshot.projectionRevision, 1);
assert.equal(subscription.snapshot.queue.hostEpoch, host.hostEpoch);
}
const firstTurnId = randomUUID();
const started = requireStartedTurn(
await desktop.startTurn({
sessionId: fixture.sessionId,
turnId: firstTurnId,
content: { text: `continuity root ${'x'.repeat(540)}` },
}),
);
for (const probe of [desktopProbe, tuiProbe]) {
const liveDelta = await probe.waitFor(
(frame) =>
frame.kind === 'subscription.session_delta' && frame.delta.turnId === firstTurnId,
'continuity did not publish the live assistant delta',
);
assert.equal(liveDelta.kind, 'subscription.session_delta');
if (liveDelta.kind === 'subscription.session_delta') {
assert.equal(liveDelta.delta.runId, started.runId);
}
}
const followupId = randomUUID();
const followupContent = { text: 'continue after the first root completes' };
const queued = await tui.request('turn.message.submit', {
originHostEpoch: host.hostEpoch,
sessionId: fixture.sessionId,
messageId: followupId,
content: followupContent,
placement: 'next_turn',
});
assert.equal(queued.disposition, 'followup');
for (const probe of [desktopProbe, tuiProbe]) {
const queueProjection = await probe.waitFor(
(frame) =>
frame.kind === 'subscription.session_projection' &&
frame.snapshot.queue.followup.some((entry) => entry.messageId === followupId),
'continuity did not publish the accepted follow-up',
);
assert.equal(queueProjection.kind, 'subscription.session_projection');
}
await desktop.close();
await desktopProbe.waitForFailure('connection_closed');
assert.equal((await tui.status()).connections, 1);
const terminal = await tuiProbe.waitFor(
(frame) =>
frame.kind === 'subscription.session_projection' &&
frame.snapshot.rootTurn?.turnId === firstTurnId &&
frame.snapshot.rootTurn.status === 'completed',
'continuity did not publish the terminal root cut',
);
assert.equal(terminal.kind, 'subscription.session_projection');
const successor = await tuiProbe.waitFor(
(frame) =>
frame.kind === 'subscription.session_projection' &&
frame.snapshot.rootTurn !== null &&
frame.snapshot.rootTurn.turnId !== firstTurnId,
'continuity did not publish the successor root',
);
assert.equal(successor.kind, 'subscription.session_projection');
if (successor.kind !== 'subscription.session_projection' || !successor.snapshot.rootTurn) {
return;
}
assert.equal(successor.snapshot.rootTurn.sessionId, fixture.sessionId);
assert.ok(tuiProbe.indexOf(terminal) < tuiProbe.indexOf(successor));
await tuiSubscription.close();
await tuiProbe.done;
await waitForTerminalTurn(tui, fixture.sessionId, successor.snapshot.rootTurn.turnId);
await tui.close();
await fixture.stopHost(host);
const chain = await fixture.readAdmissionChain();
assert.deepEqual(
chain.map((admission) => admission.turnId),
[firstTurnId, successor.snapshot.rootTurn.turnId],
);
assert.deepEqual(chain[1]?.normalizedInput, followupContent);
});
});
test('concurrent root admission for one Session has a single winner', async () => {
await withExecutionRoot(async (fixture) => {
const host = await fixture.startHost();
const first = await connectClient(fixture.root, 'desktop');
const second = await connectClient(fixture.root, 'tui');
const turnIds = [randomUUID(), randomUUID()] as const;
const outcomes = await Promise.allSettled([
first.startTurn({
sessionId: fixture.sessionId,
turnId: turnIds[0],
content: { text: FAKE_ASK_USER_QUESTION_PROMPT },
}),
second.startTurn({
sessionId: fixture.sessionId,
turnId: turnIds[1],
content: { text: FAKE_ASK_USER_QUESTION_PROMPT },
}),
]);
const winners = outcomes.filter(
(outcome): outcome is PromiseFulfilledResult<TurnStartResult> =>
outcome.status === 'fulfilled',
);
const rejected = outcomes.filter(
(outcome): outcome is PromiseRejectedResult => outcome.status === 'rejected',
);
assert.equal(winners.length, 1);
assert.equal(rejected.length, 1);
assert.ok(rejected[0]?.reason instanceof RuntimeHostOperationError);
assert.equal(rejected[0]?.reason.code, 'session_busy');
const winnerResult = winners[0]?.value;
assert.ok(winnerResult);
const winner = requireStartedTurn(winnerResult);
await first.stopTurn({
sessionId: fixture.sessionId,
turnId: winner.turnId,
runId: winner.runId,
});
await first.close();
await second.close();
await fixture.stopHost(host);
const chain = await fixture.readAdmissionChain();
assert.equal(chain.length, 1);
assert.equal(chain[0]?.turnId, winner.turnId);
assert.equal(chain[0]?.previousRootTurnId, null);
});
});
test('an archived Session rejects a new Turn before durable admission', async () => {
await withExecutionRoot(async (fixture) => {
await fixture.archiveSession();
const host = await fixture.startHost();
const client = await connectClient(fixture.root, 'desktop');
const turnId = randomUUID();
await assert.rejects(
() =>
client.startTurn({
sessionId: fixture.sessionId,
turnId,
content: { text: 'must not execute' },
}),
operationError('session_archived'),
);
assert.equal((await client.status()).state, 'ready');
await client.close();
await fixture.stopHost(host);
assert.deepEqual(await fixture.readTurnFootprint(turnId), {
admitted: false,
runCount: 0,
userMessageCount: 0,
});
});
});
test('a killed Host is recovered exactly once before its successor becomes ready', async () => {
await withExecutionRoot(async (fixture) => {
const firstHost = await fixture.startHost();
const first = await connectClient(fixture.root, 'desktop');
const firstSubscription = await first.openSessionSubscription({
sessionId: fixture.sessionId,
});
const firstProbe = new SubscriptionProbe(firstSubscription);
const turnId = randomUUID();
const started = requireStartedTurn(
await first.startTurn({
sessionId: fixture.sessionId,
turnId,
content: { text: FAKE_ASK_USER_QUESTION_PROMPT },
}),
);
await firstProbe.waitFor(
(frame) =>
frame.kind === 'subscription.session_projection' &&
frame.snapshot.rootTurn?.runId === started.runId &&
frame.snapshot.rootTurn.status !== 'admitted',
'first Host did not publish the active root projection',
);
const pending = await waitForPendingInteraction(firstSubscription, firstProbe, started.runId);
assert.equal(pending.sessionId, fixture.sessionId);
assert.equal(pending.turnId, turnId);
assert.equal(pending.runId, started.runId);
const questionRequest = pending.request;
assert.ok(questionRequest.kind === 'question');
await fixture.killHost(firstHost);
await first.closed;
await firstProbe.waitForFailure('connection_closed');
const secondHost = await fixture.startHost();
const second = await connectClient(fixture.root, 'tui');
const recoveredSubscription = await second.openSessionSubscription({
sessionId: fixture.sessionId,
});
const recovered = await second.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.equal(recovered.status, 'failed');
if (recovered.status === 'failed') assert.equal(recovered.failureClass, 'app_restarted');
assert.notEqual(recoveredSubscription.hostEpoch, firstSubscription.hostEpoch);
assert.equal(recoveredSubscription.snapshot.projectionRevision, 1);
assert.deepEqual(recoveredSubscription.snapshot.rootTurn, recovered);
assert.equal(recoveredSubscription.snapshot.queue.hostEpoch, recoveredSubscription.hostEpoch);
assert.deepEqual(recoveredSubscription.snapshot.queue.steering, []);
assert.deepEqual(recoveredSubscription.snapshot.queue.followup, []);
const closed = await second.request('interaction.query', {
sessionId: fixture.sessionId,
interactionId: pending.interactionId,
});
assert.equal(closed.sessionId, fixture.sessionId);
assert.equal(closed.turnId, turnId);
assert.equal(closed.runId, started.runId);
assert.equal(closed.status, 'closed');
assert.equal(closed.outcome.kind, 'closure');
if (closed.outcome.kind === 'closure') assert.equal(closed.outcome.reason, 'host_restarted');
await assert.rejects(
() =>
second.request('interaction.answer', {
sessionId: fixture.sessionId,
interactionId: pending.interactionId,
answer: {
kind: 'question',
answers: questionRequest.questions.map(() => null),
},
}),
operationError('already_resolved'),
);
await recoveredSubscription.close();
await second.close();
await fixture.stopHost(secondHost);
const thirdHost = await fixture.startHost();
const third = await connectClient(fixture.root, 'run');
const stable = await third.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.deepEqual(stable, recovered);
assert.equal(stable.runId, started.runId);
assert.deepEqual(
await third.request('interaction.query', {
sessionId: fixture.sessionId,
interactionId: pending.interactionId,
}),
closed,
);
await third.close();
await fixture.stopHost(thirdHost);
const ledger = await fixture.readTurn(turnId);
assert.equal(ledger.terminalEvents.length, 1);
assert.equal(ledger.classification.kind, 'fact');
if (ledger.classification.kind === 'fact') {
assert.equal(ledger.classification.fact.failureClass, 'app_restarted');
}
});
});
test('graceful Host shutdown stops and drains an active Turn before releasing ownership', async () => {
await withExecutionRoot(async (fixture) => {
const host = await fixture.startHost();
const client = await connectClient(fixture.root, 'desktop');
const turnId = randomUUID();
const started = requireStartedTurn(
await client.startTurn({
sessionId: fixture.sessionId,
turnId,
content: { text: FAKE_ASK_USER_QUESTION_PROMPT },
}),
);
const exit = await fixture.stopHost(host);
assert.deepEqual(exit, { code: 0, signal: null });
await client.closed;
const successor = await fixture.startHost();
const observer = await connectClient(fixture.root, 'tui');
const stable = await observer.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.equal(stable.runId, started.runId);
assert.equal(stable.status, 'cancelled');
await observer.close();
await fixture.stopHost(successor);
const ledger = await fixture.readTurn(turnId);
assert.equal(ledger.terminalEvents.length, 1);
assert.equal(ledger.classification.kind, 'fact');
if (ledger.classification.kind === 'fact') {
assert.equal(ledger.classification.fact.runStatus, 'cancelled');
assert.notEqual(ledger.classification.fact.failureClass, 'app_restarted');
}
});
});
test('a durable admission without a Run resumes before the Host becomes ready', async () => {
await withExecutionRoot(async (fixture) => {
const turnId = randomUUID();
const quotes = quotedContent('recover pending admission');
const { runId } = await fixture.seedAdmission(turnId, quotes);
const host = await fixture.startHost();
const client = await connectClient(fixture.root, 'tui');
const recovered = await client.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.equal(recovered.runId, runId);
assert.ok(recovered.status === 'running' || recovered.status === 'waiting_for_user');
await assert.rejects(
() =>
client.startTurn({
sessionId: fixture.sessionId,
turnId: randomUUID(),
content: { text: 'must remain behind the recovered admission' },
}),
operationError('session_busy'),
);
const stopped = await client.stopTurn(
{
sessionId: fixture.sessionId,
turnId,
runId,
},
PROCESS_TIMEOUT_MS,
);
assert.equal(stopped.status, 'cancelled');
await client.close();
await fixture.stopHost(host);
const ledger = await fixture.readTurn(turnId);
assert.equal(ledger.runs.length, 1);
assert.equal(ledger.userMessages.length, 1);
assert.deepEqual(ledger.userMessages[0]?.quotes, quotes.quotes);
assert.deepEqual(userRuntimeContent(ledger.runtimeEvents)?.quotes, quotes.quotes);
assert.equal(ledger.terminalEvents.length, 1);
assert.equal(ledger.classification.kind, 'fact');
if (ledger.classification.kind === 'fact') {
assert.notEqual(ledger.classification.fact.failureClass, 'app_restarted');
}
});
});
test('startup recovery compares an existing quoted UserMessage canonically', async () => {
await withExecutionRoot(async (fixture) => {
const turnId = randomUUID();
const content = quotedContent('recover existing message');
const { runId, userMessageId } = await fixture.seedRunWithUserMessage(turnId, content);
const host = await fixture.startHost();
const client = await connectClient(fixture.root, 'tui');
const recovered = await client.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.equal(recovered.runId, runId);
assert.equal(recovered.status, 'failed');
await client.close();
await fixture.stopHost(host);
const ledger = await fixture.readTurn(turnId);
assert.equal(ledger.userMessages.length, 1);
assert.equal(ledger.userMessages[0]?.id, userMessageId);
assert.deepEqual(ledger.userMessages[0]?.quotes, content.quotes);
assert.equal(ledger.terminalEvents.length, 1);
});
});
test('startup recovery restores the admitted UserMessage before terminalizing its Run', async () => {
await withExecutionRoot(async (fixture) => {
const turnId = randomUUID();
const { runId, userMessageId } = await fixture.seedRunWithoutUserMessage(
turnId,
'recover the admitted message',
);
const host = await fixture.startHost();
const client = await connectClient(fixture.root, 'tui');
const recovered = await client.queryTurn({
sessionId: fixture.sessionId,
turnId,
});
assert.equal(recovered.runId, runId);
assert.equal(recovered.status, 'failed');
if (recovered.status === 'failed') {
assert.equal(recovered.failureClass, 'app_restarted');
}
await client.close();
await fixture.stopHost(host);
const ledger = await fixture.readTurn(turnId);
assert.equal(ledger.runs.length, 1);
assert.equal(ledger.userMessages.length, 1);
assert.equal(ledger.userMessages[0]?.id, userMessageId);
assert.equal(ledger.terminalEvents.length, 1);
});
});
test('startup recovery canonically closes pending linked child admissions without inventing identity', async () => {
await withExecutionRoot(async (fixture) => {
const initial = await fixture.seedPendingChildAdmission('linked_child_initial');
const resume = await fixture.seedPendingChildAdmission('linked_child_resume');
const retry = await fixture.seedPendingChildAdmission('linked_child_provider_retry');
const graph = await fixture.seedPendingChildAdmission('claimed_agent_graph_intent');
const firstHost = await fixture.startHost();
await fixture.stopHost(firstHost);
const secondHost = await fixture.startHost();
await fixture.stopHost(secondHost);
const reader = await tryAcquireInteractiveRootReader(fixture.capability);
assert.ok(reader);
if (!reader) throw new Error('Unable to acquire recovery result reader');
let stores: Awaited<ReturnType<typeof openInteractiveExecutionStoresForRead>> | undefined;
try {
stores = await openInteractiveExecutionStoresForRead(reader.lease);
for (const recovered of [initial, resume, retry, graph]) {
const run = await stores.agentRunStore.readRun(recovered.sessionId, recovered.runId);
assert.equal(run.status, 'failed');
assert.equal(run.failureClass, 'app_restarted');
assert.equal(run.agentId, recovered.agentId);
assert.equal(run.agentName, recovered.agentName);
assert.equal(run.workspaceIdentity, undefined);
if (recovered.kind === 'linked_child_resume') {
assert.equal(run.resumedFromRunId, recovered.sourceRunId);
assert.equal(run.retriedFromRunId, undefined);
} else if (recovered.kind === 'linked_child_provider_retry') {
assert.equal(run.retriedFromRunId, recovered.sourceRunId);
assert.equal(run.resumedFromRunId, undefined);
} else {
assert.equal(run.resumedFromRunId, undefined);
assert.equal(run.retriedFromRunId, undefined);
}
const runtimeEvents = await stores.runtimeEventStore.readImmutableRuntimeEvents(
recovered.sessionId,
recovered.runId,
);
const terminal = classifyTerminalRuntimeLedger(run, runtimeEvents);
assert.equal(terminal.kind, 'fact');
if (terminal.kind === 'fact') {
assert.equal(terminal.fact.runStatus, 'failed');
assert.equal(terminal.fact.failureClass, 'app_restarted');
}
const userMessages: StoredMessage[] = (
await stores.sessionStore.readMessages(recovered.sessionId)
).filter((message) => message.type === 'user' && message.turnId === recovered.turnId);
assert.equal(userMessages.length, recovered.kind === 'linked_child_provider_retry' ? 0 : 1);
if (recovered.kind !== 'linked_child_provider_retry') {
assert.equal(userMessages[0]?.id, recovered.userMessageId);
}
}
} finally {
await stores?.sessionStore.close?.();
await reader.close();
}
});
});