feat(agent): add Matrix native runtime provider (#744)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful

This commit was merged in pull request #744.
This commit is contained in:
2026-07-13 15:29:37 +00:00
parent c6e3cfbd95
commit 6345dbfcf2
7 changed files with 1231 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
import { describe, expect, it, vi } from 'vitest';
import type { RuntimeScope } from '@mosaicstack/types';
import {
MatrixNativeRuntimeTransport,
type MatrixFetchLike,
} from './matrix-native-runtime-transport.js';
const scope: RuntimeScope = {
actorId: 'operator-1',
tenantId: 'tenant-a',
channelId: 'cli',
correlationId: 'corr-1',
};
const bindings = [
{
id: 'native-1',
runtimeId: '@native-worker:example.test',
roomId: '!room:example.test',
remoteUserId: '@native-worker:example.test',
state: 'active' as const,
createdAt: '2026-07-13T00:00:00.000Z',
updatedAt: '2026-07-13T00:00:00.000Z',
},
];
function response(
status: number,
body: unknown = {},
): ReturnType<MatrixFetchLike> extends Promise<infer T> ? T : never {
return {
ok: status >= 200 && status < 300,
status,
json: async () => body,
text: async () => JSON.stringify(body),
} as never;
}
function transport(fetchImpl: MatrixFetchLike): MatrixNativeRuntimeTransport {
return new MatrixNativeRuntimeTransport({
homeserverUrl: 'https://matrix.example.test/base',
accessToken: 'test-token',
userId: '@mosaic:example.test',
sessions: bindings,
fetchImpl,
});
}
describe('MatrixNativeRuntimeTransport', (): void => {
it('uses only configured session-to-room bindings and idempotency-derived Matrix transactions', async (): Promise<void> => {
const fetchImpl = vi
.fn<MatrixFetchLike>()
.mockResolvedValueOnce(response(200, { user_id: '@mosaic:example.test' }))
.mockResolvedValueOnce(response(200, { event_id: '$sent' }));
const matrix = transport(fetchImpl);
await matrix.send('native-1', { content: 'hello', idempotencyKey: 'message-1' }, scope);
expect(fetchImpl).toHaveBeenNthCalledWith(
1,
'https://matrix.example.test/base/_matrix/client/v3/account/whoami',
expect.objectContaining({
headers: expect.objectContaining({ Authorization: 'Bearer test-token' }),
}),
);
const [url, init] = fetchImpl.mock.calls[1]!;
expect(url).toMatch(
/^https:\/\/matrix\.example\.test\/base\/_matrix\/client\/v3\/rooms\/!room%3Aexample\.test\/send\/m\.room\.message\/mosaic-send-[A-Za-z0-9_-]+$/,
);
expect(init?.method).toBe('PUT');
expect(JSON.parse(init?.body ?? '')).toEqual({
msgtype: 'm.text',
body: 'hello',
'mosaic.runtime.v1': {
session_id: 'native-1',
runtime_id: '@native-worker:example.test',
actor_id: 'operator-1',
tenant_id: 'tenant-a',
channel_id: 'cli',
correlation_id: 'corr-1',
idempotency_key: 'message-1',
},
});
});
it('sends a termination only to the bound room with the exact approval reference', async (): Promise<void> => {
const fetchImpl = vi
.fn<MatrixFetchLike>()
.mockResolvedValueOnce(response(200, { user_id: '@mosaic:example.test' }))
.mockResolvedValueOnce(response(200, { event_id: '$terminated' }));
const matrix = transport(fetchImpl);
await matrix.terminate('native-1', 'approval-1', scope);
const [url, init] = fetchImpl.mock.calls[1]!;
expect(url).toMatch(
/^https:\/\/matrix\.example\.test\/base\/_matrix\/client\/v3\/rooms\/!room%3Aexample\.test\/send\/mosaic\.runtime\.terminate\/mosaic-terminate-[A-Za-z0-9_-]+$/,
);
expect(JSON.parse(init?.body ?? '')).toEqual({
session_id: 'native-1',
runtime_id: '@native-worker:example.test',
actor_id: 'operator-1',
tenant_id: 'tenant-a',
channel_id: 'cli',
correlation_id: 'corr-1',
approval_ref: 'approval-1',
});
});
it('fails closed when Matrix whoami does not match the configured native identity', async (): Promise<void> => {
const fetchImpl = vi
.fn<MatrixFetchLike>()
.mockResolvedValue(response(200, { user_id: '@other:example.test' }));
const matrix = transport(fetchImpl);
await expect(matrix.listSessions(scope)).rejects.toMatchObject({ code: 'forbidden' });
});
it('rejects unbound session IDs without using caller-supplied Matrix room data', async (): Promise<void> => {
const fetchImpl = vi
.fn<MatrixFetchLike>()
.mockResolvedValue(response(200, { user_id: '@mosaic:example.test' }));
const matrix = transport(fetchImpl);
await expect(matrix.verifySession('!attacker-room:example.test', scope)).rejects.toMatchObject({
code: 'not_found',
});
});
it('maps replay-cursor events from only the configured remote identity', async (): Promise<void> => {
const fetchImpl = vi
.fn<MatrixFetchLike>()
.mockResolvedValueOnce(response(200, { user_id: '@mosaic:example.test' }))
.mockResolvedValueOnce(
response(200, {
next_batch: 'next-cursor',
rooms: {
join: {
'!room:example.test': {
timeline: {
events: [
{
type: 'mosaic.runtime.event',
sender: '@intruder:example.test',
content: { 'mosaic.runtime.v1': { type: 'message.delta', content: 'nope' } },
},
{
type: 'mosaic.runtime.event',
sender: '@native-worker:example.test',
origin_server_ts: 1_784_246_400_000,
content: {
'mosaic.runtime.v1': {
session_id: 'native-1',
type: 'message.delta',
content: 'accepted',
},
},
},
],
},
},
},
},
}),
);
const matrix = transport(fetchImpl);
const events = [];
for await (const event of matrix.stream('native-1', 'prior-cursor', scope)) events.push(event);
expect(events).toEqual([
{
type: 'message.delta',
sessionId: 'native-1',
cursor: 'next-cursor',
occurredAt: '2026-07-17T00:00:00.000Z',
content: 'accepted',
},
]);
expect(fetchImpl.mock.calls[1]?.[0]).toContain('since=prior-cursor');
});
});

View File

@@ -0,0 +1,367 @@
import { createHash } from 'node:crypto';
import type {
RuntimeHealth,
RuntimeMessage,
RuntimeScope,
RuntimeSessionState,
RuntimeStreamEvent,
} from '@mosaicstack/types';
export type MatrixRuntimeTransportErrorCode =
| 'forbidden'
| 'invalid_request'
| 'not_found'
| 'unavailable';
export class MatrixRuntimeTransportError extends Error {
constructor(
readonly code: MatrixRuntimeTransportErrorCode,
message: string,
) {
super(message);
this.name = MatrixRuntimeTransportError.name;
}
}
/** Minimal injectable Matrix fetch surface; it avoids coupling this transport to an SDK. */
export interface MatrixFetchLike {
(
url: string,
init?: { method?: string; headers?: Record<string, string>; body?: string },
): Promise<{
ok: boolean;
status: number;
json(): Promise<unknown>;
text(): Promise<string>;
}>;
}
/** A server-configured runtime session binding. Callers never select a Matrix room. */
export interface MatrixNativeRuntimeSessionBinding {
id: string;
runtimeId: string;
roomId: string;
remoteUserId: string;
parentSessionId?: string;
state: RuntimeSessionState;
createdAt: string;
updatedAt: string;
}
export interface MatrixNativeRuntimeTransportOptions {
homeserverUrl: string;
accessToken: string;
/** Matrix user authenticated by the service token. */
userId: string;
sessions: readonly MatrixNativeRuntimeSessionBinding[];
fetchImpl?: MatrixFetchLike;
now?: () => Date;
}
interface MatrixSyncEvent {
type?: string;
sender?: string;
origin_server_ts?: number;
content?: Record<string, unknown>;
}
interface MatrixSyncResponse {
next_batch?: string;
rooms?: { join?: Record<string, { timeline?: { events?: MatrixSyncEvent[] } }> };
}
/**
* Concrete Matrix CS-API transport for the native provider. It authenticates
* the configured sender before each operation and resolves rooms exclusively
* from configured bindings, preventing client-selected room/identity routing.
*/
export class MatrixNativeRuntimeTransport {
private readonly baseUrl: string;
private readonly fetchImpl: MatrixFetchLike;
private readonly now: () => Date;
private readonly bindings: ReadonlyMap<string, MatrixNativeRuntimeSessionBinding>;
constructor(private readonly options: MatrixNativeRuntimeTransportOptions) {
this.baseUrl = validatedHomeserverUrl(options.homeserverUrl);
if (!options.accessToken.trim()) {
throw new MatrixRuntimeTransportError('invalid_request', 'Matrix access token is required');
}
if (!options.userId.trim()) {
throw new MatrixRuntimeTransportError('invalid_request', 'Matrix user identity is required');
}
this.bindings = new Map(
options.sessions.map((binding) => [binding.id, Object.freeze({ ...binding })]),
);
if (this.bindings.size !== options.sessions.length) {
throw new MatrixRuntimeTransportError(
'invalid_request',
'Matrix runtime session IDs must be unique',
);
}
this.fetchImpl = options.fetchImpl ?? (globalThis.fetch as unknown as MatrixFetchLike);
this.now = options.now ?? (() => new Date());
}
async health(_scope: RuntimeScope): Promise<RuntimeHealth> {
try {
const versions = await this.request('/_matrix/client/versions', { method: 'GET' });
if (!versions.ok) {
return this.healthResult('down', `versions HTTP ${versions.status}`);
}
await this.assertIdentity();
return this.healthResult('healthy');
} catch (error: unknown) {
return this.healthResult('down', message(error));
}
}
async listSessions(scope: RuntimeScope): Promise<MatrixNativeRuntimeSessionBinding[]> {
await this.assertIdentity();
void scope;
return [...this.bindings.values()].map((binding) => ({ ...binding }));
}
async verifySession(
sessionId: string,
scope: RuntimeScope,
): Promise<MatrixNativeRuntimeSessionBinding> {
await this.assertIdentity();
void scope;
const binding = this.bindings.get(sessionId);
if (!binding) {
throw new MatrixRuntimeTransportError(
'not_found',
'Matrix runtime session is not configured',
);
}
return { ...binding };
}
async *stream(
sessionId: string,
cursor: string | undefined,
scope: RuntimeScope,
): AsyncIterable<RuntimeStreamEvent> {
const binding = await this.verifySession(sessionId, scope);
const query = new URLSearchParams({ timeout: '0' });
if (cursor?.trim()) query.set('since', cursor);
const response = await this.request(`/_matrix/client/v3/sync?${query.toString()}`, {
method: 'GET',
headers: this.authHeaders(),
});
if (!response.ok) {
throw new MatrixRuntimeTransportError(
'unavailable',
`Matrix sync failed: HTTP ${response.status}`,
);
}
const payload = (await response.json()) as MatrixSyncResponse;
const nextCursor = payload.next_batch?.trim() || cursor?.trim() || 'initial';
const events = payload.rooms?.join?.[binding.roomId]?.timeline?.events ?? [];
for (const event of events) {
const normalized = normalizeEvent(event, binding, nextCursor);
if (normalized) yield normalized;
}
}
async send(sessionId: string, message: RuntimeMessage, scope: RuntimeScope): Promise<void> {
const binding = await this.verifySession(sessionId, scope);
const txnId = transactionId('send', binding.id, message.idempotencyKey);
const response = await this.request(
`/_matrix/client/v3/rooms/${encodeURIComponent(binding.roomId)}/send/m.room.message/${encodeURIComponent(txnId)}`,
{
method: 'PUT',
headers: this.authHeaders(),
body: JSON.stringify({
msgtype: 'm.text',
body: message.content,
'mosaic.runtime.v1': metadata(binding, scope, message.idempotencyKey),
}),
},
);
if (!response.ok) {
throw new MatrixRuntimeTransportError(
'unavailable',
`Matrix message delivery failed: HTTP ${response.status}`,
);
}
}
async terminate(sessionId: string, approvalRef: string, scope: RuntimeScope): Promise<void> {
const binding = await this.verifySession(sessionId, scope);
const txnId = transactionId('terminate', binding.id, `${approvalRef}:${scope.correlationId}`);
const response = await this.request(
`/_matrix/client/v3/rooms/${encodeURIComponent(binding.roomId)}/send/mosaic.runtime.terminate/${encodeURIComponent(txnId)}`,
{
method: 'PUT',
headers: this.authHeaders(),
body: JSON.stringify({
...metadata(binding, scope),
approval_ref: approvalRef,
}),
},
);
if (!response.ok) {
throw new MatrixRuntimeTransportError(
'unavailable',
`Matrix termination delivery failed: HTTP ${response.status}`,
);
}
}
private async assertIdentity(): Promise<void> {
let response: Awaited<ReturnType<MatrixFetchLike>>;
try {
response = await this.request('/_matrix/client/v3/account/whoami', {
method: 'GET',
headers: this.authHeaders(),
});
} catch (error: unknown) {
throw new MatrixRuntimeTransportError('unavailable', message(error));
}
if (!response.ok) {
throw new MatrixRuntimeTransportError(
'forbidden',
`Matrix whoami failed: HTTP ${response.status}`,
);
}
const body = (await response.json()) as { user_id?: unknown };
if (body.user_id !== this.options.userId) {
throw new MatrixRuntimeTransportError(
'forbidden',
'Matrix whoami identity does not match configuration',
);
}
}
private request(
path: string,
init: { method?: string; headers?: Record<string, string>; body?: string },
) {
return this.fetchImpl(`${this.baseUrl}${path}`, init);
}
private authHeaders(): Record<string, string> {
return {
Authorization: `Bearer ${this.options.accessToken}`,
'Content-Type': 'application/json',
};
}
private healthResult(status: RuntimeHealth['status'], detail?: string): RuntimeHealth {
return { status, checkedAt: this.now().toISOString(), ...(detail ? { detail } : {}) };
}
}
function metadata(
binding: MatrixNativeRuntimeSessionBinding,
scope: RuntimeScope,
idempotencyKey?: string,
): Record<string, string> {
return {
session_id: binding.id,
runtime_id: binding.runtimeId,
actor_id: scope.actorId,
tenant_id: scope.tenantId,
channel_id: scope.channelId,
correlation_id: scope.correlationId,
...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),
};
}
function transactionId(kind: 'send' | 'terminate', sessionId: string, identity: string): string {
return `mosaic-${kind}-${createHash('sha256').update(`${sessionId}\u0000${identity}`).digest('base64url')}`;
}
function normalizeEvent(
event: MatrixSyncEvent,
binding: MatrixNativeRuntimeSessionBinding,
cursor: string,
): RuntimeStreamEvent | undefined {
if (event.type !== 'mosaic.runtime.event' || event.sender !== binding.remoteUserId)
return undefined;
const payload = event.content?.['mosaic.runtime.v1'];
if (
!isRecord(payload) ||
payload['session_id'] !== binding.id ||
typeof payload['type'] !== 'string'
) {
return undefined;
}
const occurredAt =
typeof payload['occurred_at'] === 'string'
? payload['occurred_at']
: new Date(event.origin_server_ts ?? 0).toISOString();
const eventCursor = typeof payload['cursor'] === 'string' ? payload['cursor'] : cursor;
switch (payload['type']) {
case 'session.state':
return isState(payload['state'])
? {
type: 'session.state',
sessionId: binding.id,
cursor: eventCursor,
occurredAt,
state: payload['state'],
}
: undefined;
case 'message.delta':
return typeof payload['content'] === 'string'
? {
type: 'message.delta',
sessionId: binding.id,
cursor: eventCursor,
occurredAt,
content: payload['content'],
}
: undefined;
case 'message.complete':
return typeof payload['message_id'] === 'string'
? {
type: 'message.complete',
sessionId: binding.id,
cursor: eventCursor,
occurredAt,
messageId: payload['message_id'],
}
: undefined;
default:
return undefined;
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null;
}
function isState(value: unknown): value is RuntimeSessionState {
return (
value === 'starting' ||
value === 'active' ||
value === 'idle' ||
value === 'stopped' ||
value === 'failed'
);
}
function validatedHomeserverUrl(value: string): string {
let url: URL;
try {
url = new URL(value);
} catch {
throw new MatrixRuntimeTransportError(
'invalid_request',
'Matrix homeserver URL must be absolute',
);
}
if (url.protocol !== 'https:') {
throw new MatrixRuntimeTransportError(
'invalid_request',
'Matrix homeserver URL must use HTTPS',
);
}
return url.toString().replace(/\/$/, '');
}
function message(error: unknown): string {
return error instanceof Error ? error.message : 'Matrix transport request failed';
}

View File

@@ -2,6 +2,7 @@ export const VERSION = '0.0.0';
export * from './fleet/interaction-service-profile.js';
export * from './fleet/tmux-runtime-transport.js';
export * from './fleet/matrix-native-runtime-transport.js';
export {
backgroundUpdateCheck,