blob: d6744a24129ca4d0cbaa4d46c84b297d0b45f52f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { deferred } from '@maka/core/test-only/async-primitives';
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdtemp, rm } from 'node:fs/promises';
import { createServer, type Server } from 'node:net';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { test } from 'node:test';
import { setImmediate as delayImmediate, setTimeout as delay } from 'node:timers/promises';
import {
prepareStorageRootControlDirectory,
resolveStorageRoot,
} from '@maka/storage/root-authority';
import {
decodeStoredMessage as decodePersistedStoredMessage,
type StoredMessage,
} from '@maka/core/session';
import { markPersisted } from '@maka/core/persisted-value';
import {
connectRuntimeHost,
RuntimeHostSubscriptionError,
SessionRemovedSubscriptionError,
type RuntimeHostConnection,
} from '../client/index.js';
import { clientSubscription } from './fixtures/client-session-subscription.js';
import { prepareRuntimeHostEndpoint } from '../control/endpoint.js';
import { removeHostRegistration, writeHostRegistration } from '../control/registration.js';
import {
decodeClientFrame,
encodeProtocolMessage,
RUNTIME_HOST_COMPATIBILITY_EPOCH,
RUNTIME_HOST_PROTOCOL_VERSION,
RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION,
SESSION_CONTINUITY_SCHEMA_VERSION,
SESSION_TRANSCRIPT_PAGE_MAX_BYTES,
type SessionTranscriptBootstrap,
type SessionTranscriptFragment,
type SessionTranscriptPage,
type HostFrame,
type HostStatusResult,
type RequestFrame,
type SubscriptionFrame,
} from '../protocol/index.js';
import { FramedTransport } from '../transport/framed-transport.js';
import { frameLocalIpcProtocolMessage } from '../transport/local-ipc-framing.js';
const decodeStoredMessage = (value: unknown): StoredMessage =>
decodePersistedStoredMessage(markPersisted<StoredMessage>(value));
const PROTOCOL = {
min: RUNTIME_HOST_PROTOCOL_VERSION,
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
test('registers a subscription before receiving a coalesced first frame', async () => {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, 'subscription-ordered');
await writeRawLocalIpc(
transport,
Buffer.concat([
encodeLocalIpcTestFrame({
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, 1)),
]),
);
await answerClose(transport, opened.subscriptionId);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
assert.deepEqual(await subscription[Symbol.asyncIterator]().next(), {
done: false,
value: deltaFrame(connection.hostEpoch, subscription.subscriptionId, 1),
});
await subscription.close();
},
);
});
test('unobserved PTY bytes do not consume the Session iterator or sequence', async () => {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, 'subscription-pty');
const frame = {
kind: 'subscription.runtime_resource_pty_data' as const,
hostEpoch,
subscriptionId: opened.subscriptionId,
sessionId: 'session-1',
ref: 'maka://runtime/background-tasks/shell-1',
ptySequence: 7,
data: 'ready',
};
await writeRawLocalIpc(
transport,
Buffer.concat([
encodeLocalIpcTestFrame({
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
encodeLocalIpcTestFrame(frame),
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, 1)),
]),
);
await answerClose(transport, opened.subscriptionId);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
assert.deepEqual(await subscription[Symbol.asyncIterator]().next(), {
done: false,
value: deltaFrame(connection.hostEpoch, subscription.subscriptionId, 1),
});
await subscription.close();
},
);
});
test('PTY callbacks bypass a stalled Session iterator and isolate consumer failures', async () => {
const subscription = clientSubscription(
openResult('host-1', 'subscription-1'),
async () => undefined,
async () => {
throw new Error('unexpected read');
},
);
let delivered = 0;
subscription.subscribePtyData(() => {
throw new Error('broken display');
});
const unsubscribe = subscription.subscribePtyData(() => {
delivered += 1;
});
for (let ptySequence = 1; ptySequence <= 1000; ptySequence += 1) {
subscription.accept({
kind: 'subscription.runtime_resource_pty_data',
hostEpoch: 'host-1',
subscriptionId: 'subscription-1',
sessionId: 'session-1',
ref: 'maka://runtime/background-tasks/shell-1',
ptySequence,
data: 'bytes',
});
}
unsubscribe();
assert.equal(delivered, 1000);
subscription.accept(deltaFrame('host-1', 'subscription-1', 1));
assert.deepEqual(await subscription.next(), {
done: false,
value: deltaFrame('host-1', 'subscription-1', 1),
});
await subscription.close();
});
test('domain callbacks validate identity, support unsubscribe, and stop on close', async () => {
const subscription = clientSubscription(
openResult('host-1', 'subscription-domain'),
async () => undefined,
async () => {
throw new Error('unexpected read');
},
);
const domains: string[] = [];
const unsubscribe = subscription.subscribeSessionDomainChanges((frame) => {
domains.push(frame.domain);
});
subscription.accept({
kind: 'subscription.session_domain_changed',
hostEpoch: 'host-1',
subscriptionId: 'subscription-domain',
sequence: 1,
sessionId: 'session-1',
domain: 'todo',
});
assert.deepEqual(domains, ['todo']);
unsubscribe();
subscription.accept({
kind: 'subscription.session_domain_changed',
hostEpoch: 'host-1',
subscriptionId: 'subscription-domain',
sequence: 2,
sessionId: 'session-1',
domain: 'usage',
});
assert.deepEqual(domains, ['todo']);
await subscription.close();
assert.throws(
() =>
subscription.accept({
kind: 'subscription.session_domain_changed',
hostEpoch: 'host-1',
subscriptionId: 'subscription-domain',
sequence: 3,
sessionId: 'other-session',
domain: 'todo',
}),
/Session subscription is closed|Session subscription frame identity changed/,
);
});
test('isolates a sequence gap and continues requests on the same connection', async () => {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, 'subscription-gap');
await writeRawLocalIpc(
transport,
Buffer.concat([
encodeLocalIpcTestFrame({
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, 2)),
]),
);
await answerClose(transport, opened.subscriptionId);
await answerStatus(transport, hostEpoch);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
await assert.rejects(
() => subscription[Symbol.asyncIterator]().next(),
hasSubscriptionReason('sequence_gap'),
);
await assert.rejects(
// @ts-expect-error host.status must use the validated status() API.
() => connection.request('host.status', {}),
/status requires the validated status\(\) API/,
);
assert.equal((await connection.status()).hostEpoch, connection.hostEpoch);
},
);
});
test('fails the connection when status reports a different Host identity', async () => {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, 'subscription-status-identity');
await writeProtocolFrame(transport, {
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
});
await answerClose(transport, opened.subscriptionId);
await answerStatus(transport, 'different-host-epoch');
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
await subscription.close();
await assert.rejects(() => connection.status(), /status for a different Host identity/);
await connection.closed;
},
);
});
test('rejects epoch and Session correlation changes per subscription', async () => {
for (const changed of ['epoch', 'session', 'graph'] as const) {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, `subscription-${changed}`);
await writeProtocolFrame(transport, {
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
});
await writeProtocolFrame(
transport,
changed === 'graph'
? {
kind: 'subscription.agent_graph_changed',
hostEpoch,
subscriptionId: opened.subscriptionId,
sequence: 1,
rootSessionId: 'session-2',
graphId: 'agent_graph_1',
reason: 'observation',
}
: {
...deltaFrame(
changed === 'epoch' ? 'different-epoch' : hostEpoch,
opened.subscriptionId,
1,
),
...(changed === 'session' ? { sessionId: 'session-2' } : {}),
},
);
await answerClose(transport, opened.subscriptionId);
await answerStatus(transport, hostEpoch);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
await assert.rejects(
() => subscription[Symbol.asyncIterator]().next(),
hasSubscriptionReason(changed === 'epoch' ? 'host_epoch_changed' : 'correlation_changed'),
);
assert.equal((await connection.status()).hostEpoch, connection.hostEpoch);
},
);
}
});
test('evicts a locally slow iterator and keeps the connection usable', async () => {
const closeObserved = deferred<void>();
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(hostEpoch, 'subscription-slow');
const frames = [
encodeLocalIpcTestFrame({
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
];
for (let sequence = 1; sequence <= 33; sequence += 1) {
frames.push(
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, sequence)),
);
}
await writeRawLocalIpc(transport, Buffer.concat(frames));
await answerClose(transport, opened.subscriptionId, closeObserved.resolve);
await answerStatus(transport, hostEpoch);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
await closeObserved.promise;
await assert.rejects(
() => subscription[Symbol.asyncIterator]().next(),
hasSubscriptionReason('slow_consumer'),
);
assert.equal((await connection.status()).hostEpoch, connection.hostEpoch);
},
);
});
test('ends every active subscription with connection_closed on EOF', async () => {
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const request = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
await writeProtocolFrame(transport, {
requestId: request.requestId,
operation: 'subscription.open',
ok: true,
result: openResult(hostEpoch, 'subscription-eof'),
});
transport.closeAfterFlush();
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'none' },
});
await assert.rejects(
() => subscription[Symbol.asyncIterator]().next(),
hasSubscriptionReason('connection_closed'),
);
},
);
});
test('records the close reason before a full queue can reject the frame', () => {
const subscription = clientSubscription(
openResult('host-1', 'subscription-1'),
async () => undefined,
async () => {
throw new Error('unexpected read');
},
);
// Fill the client queue so the closed frame itself overflows it.
for (let sequence = 1; sequence <= 32; sequence += 1) {
subscription.accept(deltaFrame('host-1', 'subscription-1', sequence));
}
assert.throws(
() =>
subscription.accept({
kind: 'subscription.closed',
hostEpoch: 'host-1',
subscriptionId: 'subscription-1',
sequence: 33,
reason: 'session_removed',
}),
hasSubscriptionReason('slow_consumer'),
);
assert.ok(subscription.deathCause instanceof SessionRemovedSubscriptionError);
});
test('a transcript read surfaces the terminal error, not the dead-state mask', () => {
const subscription = clientSubscription(
openResult('host-1', 'subscription-1'),
async () => undefined,
async () => {
throw new Error('unexpected read');
},
);
const failure = new RuntimeHostSubscriptionError('sequence_gap', 'test gap');
subscription.fail(failure);
assert.equal(subscription.deathCause, failure);
assert.throws(
() =>
subscription.loadTranscriptPage({
direction: 'older',
throughSequence: null,
cursor: null,
anchorSequence: null,
maxBytes: 1024,
}),
(error: unknown) => error === failure,
);
});
test('loads a canonical transcript while live frames continue on the same connection', async () => {
const message = {
type: 'assistant' as const,
id: 'message-1',
turnId: 'turn-1',
ts: 1,
text: 'snapshot text',
modelId: 'test-model',
};
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const openRequest = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
const opened = openResult(
hostEpoch,
'subscription-transcript',
transcriptBootstrap(Buffer.from(JSON.stringify(message), 'utf8')),
);
await writeRawLocalIpc(
transport,
Buffer.concat([
encodeLocalIpcTestFrame({
requestId: openRequest.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, 1)),
]),
);
await answerClose(transport, opened.subscriptionId);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'tail', maxBytes: 16 * 1024 },
});
assert.deepEqual(await subscription.loadTranscript(decodeStoredMessage), [message]);
assert.deepEqual(await subscription[Symbol.asyncIterator]().next(), {
done: false,
value: deltaFrame(connection.hostEpoch, subscription.subscriptionId, 1),
});
await subscription.close();
},
);
});
test('resumes bounded index preparation before publishing the canonical transcript', async () => {
const message = {
type: 'assistant' as const,
id: 'message-1',
turnId: 'turn-1',
ts: 1,
text: 'snapshot text',
modelId: 'test-model',
};
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
let openRequest = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
for (let batch = 0; batch < 3; batch++) {
await writeProtocolFrame(transport, {
requestId: openRequest.requestId,
operation: 'subscription.open',
ok: false,
error: { code: 'transcript_preparing', message: `indexed through ${batch * 64}` },
});
const next = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in next) && next.operation === 'subscription.open');
assert.deepEqual(next.input, openRequest.input);
openRequest = next;
}
const opened = openResult(
hostEpoch,
'subscription-transcript',
transcriptBootstrap(Buffer.from(JSON.stringify(message), 'utf8')),
);
await writeRawLocalIpc(
transport,
Buffer.concat([
encodeLocalIpcTestFrame({
requestId: openRequest.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
}),
encodeLocalIpcTestFrame(deltaFrame(hostEpoch, opened.subscriptionId, 1)),
]),
);
await answerClose(transport, opened.subscriptionId);
},
async (connection) => {
const subscription = await connection.openSessionSubscription({
sessionId: 'session-1',
transcript: { kind: 'tail', maxBytes: 16 * 1024 },
});
assert.deepEqual(await subscription.loadTranscript(decodeStoredMessage), [message]);
assert.deepEqual(await subscription[Symbol.asyncIterator]().next(), {
done: false,
value: deltaFrame(connection.hostEpoch, subscription.subscriptionId, 1),
});
await subscription.close();
},
);
});
test('reassembles bounded backward pages with a timeout independent of index preparation', async () => {
const message = {
type: 'user' as const,
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'hello',
};
const encoded = Buffer.from(JSON.stringify(message), 'utf8');
const splitAt = Math.floor(encoded.byteLength / 2);
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
let openRequest = await acceptConnectionAndReadOpen(transport, hostEpoch, rootId);
await new Promise<void>((resolve) => setTimeout(resolve, 700));
await writeProtocolFrame(transport, {
requestId: openRequest.requestId,
operation: 'subscription.open',
ok: false,
error: { code: 'transcript_preparing', message: 'Preparing history' },
});
const next = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in next) && next.operation === 'subscription.open');
openRequest = next;
const opened = openResult(hostEpoch, 'subscription-fragmented', {
durable: transcriptPage({
rawBytes: encoded.byteLength - splitAt,
fragments: [
{
sequence: 0,
byteOffset: splitAt,
totalBytes: encoded.byteLength,
payloadDigest: null,
data: encoded.subarray(splitAt).toString('base64'),
},
],
nextCursor: 'cursor-1',
}),
});
await writeProtocolFrame(transport, {
requestId: openRequest.requestId,
operation: 'subscription.open',
ok: true,
result: opened,
});
const continuationRequest = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in continuationRequest));
assert.equal(continuationRequest.operation, 'session.transcript.page');
assert.deepEqual(continuationRequest.input, {
subscriptionId: opened.subscriptionId,
direction: 'older',
throughSequence: 0,
cursor: 'cursor-1',
anchorSequence: null,
maxBytes: SESSION_TRANSCRIPT_PAGE_MAX_BYTES,
});
await new Promise<void>((resolve) => setTimeout(resolve, 500));
await writeProtocolFrame(transport, {
requestId: continuationRequest.requestId,
operation: 'session.transcript.page',
ok: true,
result: transcriptPage({
rawBytes: splitAt,
fragments: [
{
sequence: 0,
byteOffset: 0,
totalBytes: encoded.byteLength,
payloadDigest: null,
data: encoded.subarray(0, splitAt).toString('base64'),
},
],
}),
});
await answerClose(transport, opened.subscriptionId);
},
async (connection) => {
const subscription = await connection.openSessionSubscription(
{
sessionId: 'session-1',
transcript: { kind: 'tail', maxBytes: 16 * 1024 },
},
1_000,
);
assert.deepEqual(await subscription.loadTranscript(decodeStoredMessage), [message]);
await subscription.close();
},
);
});
test('decodes one bounded page without walking the remaining transcript', async () => {
const message = {
type: 'user' as const,
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'hello',
};
const encoded = Buffer.from(JSON.stringify(message), 'utf8');
const splitAt = Math.floor(encoded.byteLength / 2);
const requests: Array<{ cursor: string | null; maxBytes: number }> = [];
const subscription = clientSubscription(
openResult('host-1', 'subscription-bounded-page', {
durable: {
...transcriptPage({
rawBytes: encoded.byteLength - splitAt,
fragments: [
{
sequence: 4,
byteOffset: splitAt,
totalBytes: encoded.byteLength,
payloadDigest: null,
data: encoded.subarray(splitAt).toString('base64'),
},
],
nextCursor: 'complete-message',
}),
throughSequence: 4,
},
}),
async () => undefined,
async (input) => {
requests.push({ cursor: input.cursor, maxBytes: input.maxBytes });
return {
...transcriptPage({
rawBytes: splitAt,
fragments: [
{
sequence: 4,
byteOffset: 0,
totalBytes: encoded.byteLength,
payloadDigest: null,
data: encoded.subarray(0, splitAt).toString('base64'),
},
],
nextCursor: 'older-records',
}),
throughSequence: 4,
};
},
);
const assemblyDeltas: number[] = [];
const decoded = await subscription.decodeTranscriptPage(
subscription.transcriptBootstrap!.durable,
decodeStoredMessage,
undefined,
(deltaBytes) => assemblyDeltas.push(deltaBytes),
);
assert.deepEqual(decoded, {
messages: [{ identity: 4, message }],
nextCursor: 'older-records',
});
assert.deepEqual(requests, [{ cursor: 'complete-message', maxBytes: splitAt }]);
assert.deepEqual(assemblyDeltas, [encoded.byteLength, -encoded.byteLength]);
requests.length = 0;
await assert.rejects(
subscription.decodeTranscriptPage(
subscription.transcriptBootstrap!.durable,
decodeStoredMessage,
encoded.byteLength - 1,
),
RangeError,
);
assert.deepEqual(requests, []);
});
test('returns a page of complete messages without reading past its cursor', async () => {
const prompt = {
type: 'user' as const,
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'prompt',
};
const promptBytes = Buffer.from(JSON.stringify(prompt), 'utf8');
const requests: string[] = [];
const initial: SessionTranscriptPage = {
...transcriptPage({
rawBytes: promptBytes.byteLength,
fragments: [
{
sequence: 0,
byteOffset: 0,
totalBytes: promptBytes.byteLength,
payloadDigest: null,
data: promptBytes.toString('base64'),
},
],
nextCursor: 'answer',
}),
direction: 'newer',
throughSequence: 1,
};
const subscription = clientSubscription(
openResult('host-1', 'subscription-newer-turn', {
durable: initial,
}),
async () => undefined,
async (input) => {
requests.push(input.cursor!);
throw new Error('a complete page must not read its continuation');
},
);
const decoded = await subscription.decodeTranscriptPage(initial, decodeStoredMessage);
assert.deepEqual(
decoded.messages.map(({ identity, message }) => [identity, message.id]),
[[0, 'user-1']],
);
assert.equal(decoded.nextCursor, 'answer');
assert.deepEqual(requests, []);
});
test('loads a durable transcript whose sequences are sparse', async () => {
const messages = [0, 2].map((sequence) =>
Buffer.from(
JSON.stringify({
type: 'user',
id: `user-${sequence}`,
turnId: 'turn-1',
ts: sequence + 1,
text: `visible-${sequence}`,
}),
'utf8',
),
);
const subscription = clientSubscription(
openResult('host-1', 'subscription-projected', {
durable: {
...transcriptPage({
rawBytes: messages.reduce((total, message) => total + message.byteLength, 0),
fragments: messages
.map((message, index) => ({
sequence: index * 2,
byteOffset: 0,
totalBytes: message.byteLength,
payloadDigest: null,
data: message.toString('base64'),
}))
.reverse(),
}),
throughSequence: 2,
},
}),
async () => undefined,
async () => {
throw new Error('unexpected page request');
},
);
assert.deepEqual(
(await subscription.loadTranscript(decodeStoredMessage)).map((message) => message.id),
['user-0', 'user-2'],
);
});
test('rejects a durable message that does not match its payload digest', async () => {
const message = Buffer.from(
JSON.stringify({
type: 'user',
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'hello',
}),
'utf8',
);
const subscription = clientSubscription(
openResult('host-1', 'subscription-digest-mismatch', {
durable: transcriptPage({
rawBytes: message.byteLength,
fragments: [
{
sequence: 0,
byteOffset: 0,
totalBytes: message.byteLength,
payloadDigest: `sha256:${createHash('sha256').update('different').digest('hex')}`,
data: message.toString('base64'),
},
],
}),
}),
async () => undefined,
async () => {
throw new Error('unexpected page request');
},
);
const assemblyDeltas: number[] = [];
await assert.rejects(
() =>
subscription.decodeTranscriptPage(
subscription.transcriptBootstrap!.durable,
decodeStoredMessage,
undefined,
(deltaBytes) => assemblyDeltas.push(deltaBytes),
),
hasSubscriptionReason('correlation_changed'),
);
assert.deepEqual(assemblyDeltas, [message.byteLength, -message.byteLength]);
});
test('rejects a transcript cursor that does not advance', async () => {
const message = Buffer.from(
JSON.stringify({
type: 'user',
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'hello',
}),
'utf8',
);
const repeated = transcriptPage({
rawBytes: message.byteLength,
nextCursor: 'stuck-cursor',
fragments: [
{
sequence: 0,
byteOffset: 0,
totalBytes: message.byteLength,
payloadDigest: null,
data: message.toString('base64'),
},
],
});
const subscription = clientSubscription(
openResult('host-1', 'subscription-stuck-cursor', {
durable: repeated,
}),
async () => undefined,
async () => repeated,
);
await assert.rejects(subscription.loadTranscript(decodeStoredMessage), {
name: 'RuntimeHostSubscriptionError',
reason: 'correlation_changed',
message: 'Session transcript cursor did not advance',
});
});
test('close stops transcript pagination after the in-flight page', async () => {
const message = Buffer.from(
JSON.stringify({
type: 'user',
id: 'user-1',
turnId: 'turn-1',
ts: 1,
text: 'hello',
}),
'utf8',
);
const page = deferred<ReturnType<typeof transcriptPage>>();
let pageRequests = 0;
const subscription = clientSubscription(
openResult('host-1', 'subscription-closing', {
durable: transcriptPage({
rawBytes: Math.floor(message.byteLength / 2),
fragments: [
{
sequence: 0,
byteOffset: Math.ceil(message.byteLength / 2),
totalBytes: message.byteLength,
payloadDigest: null,
data: message.subarray(Math.ceil(message.byteLength / 2)).toString('base64'),
},
],
nextCursor: 'cursor-1',
}),
}),
async () => undefined,
async () => {
pageRequests += 1;
return page.promise;
},
);
const loading = subscription.loadTranscript(decodeStoredMessage);
await delayImmediate();
await subscription.close();
page.resolve(transcriptPage());
await assert.rejects(() => loading, hasSubscriptionReason('connection_closed'));
assert.equal(pageRequests, 1);
});
test('probes an otherwise idle accepted Runtime Host connection', { timeout: 2_000 }, async () => {
const probed = deferred<void>();
const observed = deferred<HostStatusResult>();
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const hello = decodeClientFrame(await transport.read(1_000));
assert.ok('kind' in hello && hello.kind === 'hello');
await writeProtocolFrame(transport, {
kind: 'accepted',
rootId,
hostEpoch,
connectionId: 'connection-idle-liveness',
selectedProtocol: RUNTIME_HOST_PROTOCOL_VERSION,
compatibilityEpoch: RUNTIME_HOST_COMPATIBILITY_EPOCH,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
});
await answerStatus(transport, hostEpoch);
},
async () => {
const status = await observed.promise;
assert.equal(status.state, 'ready');
assert.equal(status.compositionId, 'maka.interactive');
await probed.promise;
},
{
livenessIntervalMs: 20,
onLivenessProbe: probed.resolve,
onHostStatus: observed.resolve,
},
);
});
test('tolerates a short Host stall without abandoning the connection', {
timeout: 5_000,
}, async () => {
const observed = deferred<HostStatusResult>();
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
const hello = decodeClientFrame(await transport.read(1_000));
assert.ok('kind' in hello && hello.kind === 'hello');
await writeProtocolFrame(transport, {
kind: 'accepted',
rootId,
hostEpoch,
connectionId: 'connection-active-liveness',
selectedProtocol: RUNTIME_HOST_PROTOCOL_VERSION,
compatibilityEpoch: RUNTIME_HOST_COMPATIBILITY_EPOCH,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
});
const probe = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in probe));
assert.equal(probe.operation, 'host.status');
// A transient pause beyond the old two-second deadline is recoverable.
// Only the eventual matching response completes the probe.
await delay(1_100);
await writeProtocolFrame(transport, {
kind: 'session.catalog.changed',
revision: 1,
sessionId: 'shared-session',
});
await delay(1_100);
await writeProtocolFrame(transport, {
requestId: probe.requestId,
operation: 'host.status',
ok: true,
result: hostStatus(hostEpoch),
});
await answerStatus(transport, hostEpoch);
},
async (connection) => {
await observed.promise;
assert.equal((await connection.status()).hostEpoch, connection.hostEpoch);
},
{
livenessIntervalMs: 20,
onHostStatus: observed.resolve,
},
);
});
test('closes an unresponsive request path even while Host notifications continue', {
timeout: 12_000,
}, async (t) => {
let received = 0;
let probes = 0;
const probeReceived = deferred<void>();
const notificationsReceived = deferred<void>();
const finalNotificationReceived = deferred<void>();
let sendFinalNotification!: () => Promise<void>;
await withProtocolPeer(
async (transport, hostEpoch, rootId) => {
await transport.read(1_000);
await writeProtocolFrame(transport, {
kind: 'accepted',
rootId,
hostEpoch,
connectionId: 'one-way-host',
selectedProtocol: RUNTIME_HOST_PROTOCOL_VERSION,
compatibilityEpoch: RUNTIME_HOST_COMPATIBILITY_EPOCH,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
});
let revision = 0;
sendFinalNotification = () =>
writeProtocolFrame(transport, {
kind: 'session.catalog.changed',
revision: ++revision,
sessionId: 'final-notification',
});
const notifications = setInterval(() => {
void writeProtocolFrame(transport, {
kind: 'session.catalog.changed',
revision: ++revision,
sessionId: 'shared-session',
}).catch(() => undefined);
}, 10);
try {
const probe = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in probe));
assert.equal(probe.operation, 'host.status');
probeReceived.resolve();
await transport.closed;
} finally {
clearInterval(notifications);
}
},
async (connection) => {
let closed = false;
void connection.closed.then(() => {
closed = true;
});
connection.subscribeSessionCatalogChanges((event) => {
received += 1;
if (received > 10) notificationsReceived.resolve();
if (event.sessionId === 'final-notification') finalNotificationReceived.resolve();
});
t.mock.timers.tick(20);
await probeReceived.promise;
await notificationsReceived.promise;
t.mock.timers.tick(7_999);
await sendFinalNotification().catch(() => undefined);
await Promise.race([finalNotificationReceived.promise, connection.closed]);
assert.equal(closed, false, 'inbound events must not end the pending probe early');
t.mock.timers.tick(1);
await connection.closed;
assert.ok(received > 10, 'inbound events must remain active during the failed probe');
assert.equal(probes, 0, 'one-way events cannot acknowledge a probe');
},
{
livenessIntervalMs: 20,
onLivenessProbe: () => {
probes += 1;
},
},
() => t.mock.timers.enable({ apis: ['setTimeout'] }),
);
});
async function withProtocolPeer(
serve: (transport: FramedTransport, hostEpoch: string, rootId: string) => Promise<void>,
run: (connection: RuntimeHostConnection) => Promise<void>,
connectionOptions: {
readonly livenessIntervalMs?: number;
readonly onLivenessProbe?: () => void;
readonly onHostStatus?: (status: HostStatusResult) => void;
} = {},
beforeConnect?: () => void,
): Promise<void> {
const base = await mkdtemp(join(tmpdir(), 'maka-runtime-host-subscription-'));
const capability = await resolveStorageRoot({
path: join(base, 'root'),
kind: 'interactive',
});
const { controlDirectory } = await prepareStorageRootControlDirectory(capability);
const hostEpoch = randomUUID();
const endpoint = await prepareRuntimeHostEndpoint({
rootId: capability.rootId,
hostEpoch,
});
const serverTask = deferred<void>();
const server = createServer((socket) => {
void serve(new FramedTransport(socket), hostEpoch, capability.rootId).then(
serverTask.resolve,
serverTask.reject,
);
});
try {
await listen(server, endpoint.path);
await endpoint.prepareAfterListen();
await writeHostRegistration(controlDirectory, {
kind: 'maka-runtime-host',
schemaVersion: RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION,
rootId: capability.rootId,
hostEpoch,
endpoint: endpoint.path,
protocolMin: RUNTIME_HOST_PROTOCOL_VERSION,
protocolMax: RUNTIME_HOST_PROTOCOL_VERSION,
compatibilityEpoch: RUNTIME_HOST_COMPATIBILITY_EPOCH,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
pid: process.pid,
createdAt: new Date().toISOString(),
});
beforeConnect?.();
const connected = await connectRuntimeHost({
rootPath: join(base, 'root'),
protocol: PROTOCOL,
...connectionOptions,
});
assert.equal(connected.kind, 'connected');
if (connected.kind !== 'connected') return;
try {
await run(connected.connection);
} finally {
await connected.connection.close();
}
await serverTask.promise;
} finally {
await closeServer(server);
await removeHostRegistration(controlDirectory, hostEpoch).catch(() => undefined);
await endpoint.cleanup().catch(() => undefined);
await rm(base, { recursive: true, force: true });
}
}
async function acceptConnectionAndReadOpen(
transport: FramedTransport,
hostEpoch: string,
rootId: string,
): Promise<Extract<RequestFrame, { operation: 'subscription.open' }>> {
const hello = decodeClientFrame(await transport.read(1_000));
assert.ok('kind' in hello && hello.kind === 'hello');
await writeProtocolFrame(transport, {
kind: 'accepted',
rootId,
hostEpoch,
connectionId: 'connection-1',
selectedProtocol: RUNTIME_HOST_PROTOCOL_VERSION,
compatibilityEpoch: RUNTIME_HOST_COMPATIBILITY_EPOCH,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
});
const request = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in request));
assert.equal(request.operation, 'subscription.open');
return request as Extract<RequestFrame, { operation: 'subscription.open' }>;
}
async function answerClose(
transport: FramedTransport,
subscriptionId: string,
onObserved?: () => void,
): Promise<void> {
const request = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in request));
assert.equal(request.operation, 'subscription.close');
assert.deepEqual(request.input, { subscriptionId });
onObserved?.();
await writeProtocolFrame(transport, {
requestId: request.requestId,
operation: 'subscription.close',
ok: true,
result: { subscriptionId },
});
}
async function answerStatus(transport: FramedTransport, hostEpoch: string): Promise<void> {
const request = decodeClientFrame(await transport.read(1_000));
assert.ok(!('kind' in request));
assert.equal(request.operation, 'host.status');
await writeProtocolFrame(transport, {
requestId: request.requestId,
operation: 'host.status',
ok: true,
result: hostStatus(hostEpoch),
});
}
function hostStatus(hostEpoch: string): HostStatusResult {
return {
hostEpoch,
compositionId: 'maka.interactive',
compositionRevision: '1',
state: 'ready',
connections: 1,
activeOperations: 1,
activeResidencies: 0,
};
}
function openResult(
hostEpoch: string,
subscriptionId: string,
transcript: SessionTranscriptBootstrap | null = null,
) {
return {
hostEpoch,
subscriptionId,
nextSequence: 1,
activeAssistantStreams: [],
transcript,
snapshot: {
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: 'session-1',
metadataRevision: 1,
status: 'running' as const,
createdAt: 1,
isArchived: false,
},
projectionRevision: 1,
rootTurn: {
sessionId: 'session-1',
turnId: 'turn-1',
runId: 'run-1',
status: 'running' as const,
},
goal: null,
queue: { hostEpoch, queueRevision: 1, steering: [], followup: [] },
interactions: { pending: [] },
},
};
}
function transcriptBootstrap(message: Buffer): SessionTranscriptBootstrap {
return {
durable: transcriptPage({
rawBytes: message.byteLength,
fragments: [
{
sequence: 0,
byteOffset: 0,
totalBytes: message.byteLength,
payloadDigest: null,
data: message.toString('base64'),
},
],
}),
};
}
function transcriptPage(
options: {
rawBytes?: number;
fragments?: readonly SessionTranscriptFragment[];
nextCursor?: string | null;
endsAtTurnBoundary?: boolean;
} = {},
): SessionTranscriptPage {
return {
kind: 'page',
sessionId: 'session-1',
direction: 'older',
throughSequence: 0,
rawBytes: options.rawBytes ?? 0,
fragments: options.fragments ?? [],
nextCursor: options.nextCursor ?? null,
endsAtTurnBoundary: options.endsAtTurnBoundary ?? options.nextCursor == null,
};
}
function deltaFrame(
hostEpoch: string,
subscriptionId: string,
sequence: number,
): SubscriptionFrame {
return {
kind: 'subscription.session_delta',
hostEpoch,
subscriptionId,
sequence,
sessionId: 'session-1',
delta: {
kind: 'text',
turnId: 'turn-1',
runId: 'run-1',
messageId: 'message-1',
startOffset: 0,
text: `chunk-${sequence}`,
},
};
}
function hasSubscriptionReason(reason: RuntimeHostSubscriptionError['reason']) {
return (error: unknown) =>
error instanceof RuntimeHostSubscriptionError && error.reason === reason;
}
function writeProtocolFrame(transport: FramedTransport, frame: HostFrame): Promise<void> {
return transport.write(encodeProtocolMessage(frame));
}
function encodeLocalIpcTestFrame(frame: HostFrame): Buffer {
return frameLocalIpcProtocolMessage(encodeProtocolMessage(frame));
}
function writeRawLocalIpc(transport: FramedTransport, frame: Uint8Array): Promise<void> {
return new Promise((resolve, reject) => {
transport.socket.write(frame, (error) => (error ? reject(error) : resolve()));
});
}
function listen(server: Server, path: string): Promise<void> {
return new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(path, resolve);
});
}
function closeServer(server: Server): Promise<void> {
if (!server.listening) return Promise.resolve();
return new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
}