blob: 1e1c8030a43fa8cda54ef65b67b1127e9c5c79e5 [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 { randomUUID } from 'node:crypto';
import { isDeepStrictEqual } from 'node:util';
import type { SessionEvent, ShellRunUpdate } from '@maka/core/events';
import { projectToolArgsPreview } from '@maka/core/tool-quiet-preview';
import { resolveReadInput } from '@maka/runtime/read-page';
import {
decodeRuntimeResourceRef,
encodeProtocolMessage,
RUNTIME_HOST_MAX_MESSAGE_BYTES,
SESSION_LIVE_DELTA_MAX_BYTES,
SESSION_RUNTIME_RESOURCE_PTY_DATA_MAX_BYTES,
SESSION_RUNTIME_RESOURCE_CHANGES_MAX,
SESSION_SUBSCRIPTION_FRAME_MAX_BYTES,
SUBSCRIPTION_OPEN_RESULT_MAX_BYTES,
SESSION_TOOL_ARGS_PREVIEW_MAX_BYTES,
SESSION_TOOL_INTENT_MAX_BYTES,
SESSION_TOOL_NAME_MAX_BYTES,
type AgentGraphChangedFrame,
type AgentGraphChangedReason,
type SessionAssistantDelta,
type SessionContinuitySnapshot,
type SessionDeltaFrame,
type SessionDomainChange,
type SessionDomainChangedFrame,
type SessionEventFrame,
type SessionRuntimeResourcePtyDataFrame,
type OrderedSubscriptionFrame,
type SessionSteeringEvent,
type SessionToolEvent,
type SessionTranscriptAdvancedFrame,
type SessionTranscriptPageInput,
type OperationOutcome,
type SubscriptionFrame,
type SubscriptionOpenInput,
type SubscriptionOpenResult,
type LiveTurnSnapshot,
type TurnProviderRetry,
type TurnSnapshot,
} from '../protocol/index.js';
import type {
ConnectionContext,
SessionContinuityOperationHandlerMap,
} from './operation-dispatcher.js';
import type { RuntimeHostAccessAuthority } from './access-authority.js';
import { type SessionAdmissionLease, SessionAdmissionGate } from './session-admission-gate.js';
import {
type CanonicalSessionProjection,
createSessionContinuitySnapshot,
} from './canonical-session-projection.js';
import type {
SessionContinuityConnection,
SessionContinuityFrameSink,
SessionContinuityService,
} from './session-continuity-service.js';
import {
createSessionTranscriptBootstrap,
prepareSessionTranscriptOverlay,
readSessionTranscriptPage,
type SubscriberTranscriptState,
TranscriptPageRequestError,
updateSubscriberTranscriptHighWater,
} from './session-transcript-pager.js';
import {
ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES,
type SessionTranscriptReader,
} from './session-transcript-reader.js';
import { projectSharedSessionMessageContent } from './shared-session-transcript.js';
const MAX_CONNECTION_SUBSCRIPTIONS = 16;
const MAX_SUBSCRIBER_QUEUED_FRAMES = 32;
const MAX_SUBSCRIBER_QUEUED_BYTES = 256 * 1024;
export type { CanonicalSessionProjection } from './canonical-session-projection.js';
export type RuntimeSessionForwardedEvent = Extract<
SessionEvent,
{
type:
| 'text_delta'
| 'text_complete'
| 'thinking_delta'
| 'thinking_complete'
| 'tool_start'
| 'tool_output_delta'
| 'tool_progress'
| 'tool_result_preview'
| 'tool_result'
| 'steering_message'
| 'provider_retry';
}
>;
export type ReadCanonicalSessionProjection = (
sessionId: string,
) => Promise<CanonicalSessionProjection | null>;
interface SessionProjectionState {
canonical: CanonicalSessionProjection;
revision: number;
subscribers: Map<string, Subscriber>;
assistantStreams: Map<string, ActiveAssistantStream>;
transcriptOverlay?: CachedTranscriptOverlay;
/**
* Latest live tool_result_preview per toolUseId for the active turn.
* Replace semantics; cleared on tool_result and terminal publication.
* Seeded to new subscribers so mid-flight Open survives rejoin.
*/
toolResultPreviews: Map<
string,
Extract<RuntimeSessionForwardedEvent, { type: 'tool_result_preview' }>
>;
terminalPublicationFence?: TerminalPublicationFence;
}
interface ActiveAssistantStream {
turnId: string;
messageId: string;
kind: SessionAssistantDelta['kind'];
text: string;
completedParts?: string[];
}
interface TerminalPublicationFence {
turnId: string;
runId: string;
}
interface ConnectionState {
sink: SessionContinuityFrameSink;
subscriptionIds: Set<string>;
pendingOpenCount: number;
readonly closed: AbortController;
}
interface QueuedSubscriptionFrame {
frame: OrderedSubscriptionFrame;
encodedBytes: number;
}
interface Subscriber {
connectionId: string;
principalId: string;
principalKind: NonNullable<ConnectionContext['principalKind']>;
sessionId: string;
subscriptionId: string;
sink: SessionContinuityFrameSink;
phase: 'open' | 'closing' | 'closed';
activated: boolean;
nextSequence: number;
lastFlushedSequence: number;
queue: QueuedSubscriptionFrame[];
queuedBytes: number;
pumping: boolean;
ptyQueue: { frame: SessionRuntimeResourcePtyDataFrame; encodedBytes: number }[];
ptyQueuedBytes: number;
ptyPumping: boolean;
ptyInterests: Set<string>;
terminalQueued: boolean;
transcript?: SubscriberTranscriptState;
retainedTranscriptOverlay?: RetainedTranscriptOverlay;
}
interface RetainedTranscriptOverlay {
readonly messages: readonly Buffer[];
readonly bytes: number;
references: number;
}
interface CachedTranscriptOverlay {
readonly throughSequence: number | null;
readonly prepared: Promise<RetainedTranscriptOverlay>;
pendingConsumers: number;
cancelPreparation(): void;
}
interface TranscriptOverlayPreparationWaiter {
cancelled: boolean;
granted: boolean;
resolve(release: () => void): void;
reject(error: Error): void;
}
interface TranscriptOverlayPreparationPermit {
readonly waiter: TranscriptOverlayPreparationWaiter;
take(): () => void;
release(): void;
}
const MAX_RETAINED_TRANSCRIPT_OVERLAY_BYTES = 64 * 1024 * 1024;
const MAX_TRANSCRIPT_OVERLAY_PREPARATION_WAITERS = 64;
// Preparation can retain the active projection, durable reconciliation, and
// final encoded snapshot at the same time; charge all three to the Host budget.
const MAX_TRANSCRIPT_OVERLAY_PREPARATION_BYTES = ACTIVE_TRANSCRIPT_OVERLAY_MAX_BYTES * 3;
class TranscriptOverlayCapacityError extends Error {
readonly name = 'TranscriptOverlayCapacityError';
}
class TranscriptOverlayPreparationRequired extends Error {
readonly name = 'TranscriptOverlayPreparationRequired';
}
interface PendingRefresh {
dirty: boolean;
inFlight: boolean;
}
interface PendingAgentGraphChange {
event: {
rootSessionId: string;
graphId: string;
reason: AgentGraphChangedReason;
};
}
type SessionProjectionDomain = Exclude<SessionDomainChange['domain'], 'runtime_resource'>;
interface PendingSessionDomainChanges {
readonly domains: Set<SessionProjectionDomain>;
readonly runtimeResources: Map<string, { sourceSessionId: string; ref: string }>;
}
export class SessionContinuityCoordinator implements SessionContinuityService {
readonly handlers: SessionContinuityOperationHandlerMap = {
'subscription.pty_interest.set': async (input, context) => {
const subscriber = this.#ownedSubscriber(context.connectionId, input.subscriptionId);
if (!subscriber || !this.#canObserve(subscriber, subscriber.sessionId)) {
return {
ok: false,
error: { code: 'not_found', message: 'Session subscription was not found' },
};
}
subscriber.ptyInterests = new Set(input.refs);
// An already writing frame may finish. Everything else belongs to the
// current visible set; reacquiring uses a fresh terminal snapshot.
subscriber.ptyQueue = subscriber.ptyQueue.filter(
(entry, index) =>
(index === 0 && subscriber.ptyPumping) || subscriber.ptyInterests.has(entry.frame.ref),
);
subscriber.ptyQueuedBytes = subscriber.ptyQueue.reduce(
(bytes, entry) => bytes + entry.encodedBytes,
0,
);
return { ok: true, result: { subscriptionId: input.subscriptionId } };
},
'subscription.open': async (input, context) => {
const result = await this.#open(context, input);
return result.ok
? { ok: true, result: result.value }
: { ok: false, error: { code: result.code, message: result.message } };
},
'subscription.close': async (input, context) => {
const closed = this.#closeSubscription(context.connectionId, input.subscriptionId);
return closed
? { ok: true, result: { subscriptionId: input.subscriptionId } }
: {
ok: false,
error: { code: 'not_found', message: 'Session subscription was not found' },
};
},
'session.transcript.page': (input, context) =>
this.#readTranscriptPage(context.connectionId, input),
'session.transcript.overlay.release': async (input, context) => {
const existing = this.#subscriptions.get(input.subscriptionId);
if (!existing) {
return { ok: true, result: { subscriptionId: input.subscriptionId } };
}
const subscriber = this.#ownedSubscriber(context.connectionId, input.subscriptionId);
if (!subscriber) {
return {
ok: false,
error: { code: 'not_found', message: 'Session subscription was not found' },
};
}
this.#releaseSubscriberTranscriptOverlay(subscriber);
return { ok: true, result: { subscriptionId: input.subscriptionId } };
},
};
readonly #connections = new Map<string, ConnectionState>();
readonly #sessions = new Map<string, SessionProjectionState>();
readonly #subscriptions = new Map<string, Subscriber>();
readonly #pendingRefreshes = new Map<string, PendingRefresh>();
readonly #pendingAgentGraphChanges = new Map<string, PendingAgentGraphChange>();
readonly #pendingSessionDomainChanges = new Map<string, PendingSessionDomainChanges>();
readonly #retainedTranscriptOverlays = new Map<readonly Buffer[], RetainedTranscriptOverlay>();
readonly #transcriptOverlayPreparationWaiters: TranscriptOverlayPreparationWaiter[] = [];
readonly #hostEpoch: string;
readonly #readCanonical: ReadCanonicalSessionProjection;
readonly #transcriptReader: SessionTranscriptReader | undefined;
#closed = false;
#preparingTranscriptOverlayBytes = 0;
#retainedTranscriptOverlayBytes = 0;
readonly #sessionAccessAuthority:
| Pick<RuntimeHostAccessAuthority, 'activeSessionGrant' | 'subscribeGrantRevocations'>
| undefined;
readonly #unsubscribeGrantRevocations: (() => void) | undefined;
constructor(
hostEpoch: string,
readCanonical: ReadCanonicalSessionProjection,
private readonly sessionAdmission: SessionAdmissionGate,
private readonly onPublicationFailure: (error: unknown) => void = () => undefined,
transcriptReader?: SessionTranscriptReader,
private readonly onCatalogChanged: (sessionId: string) => void = () => undefined,
sessionAccessAuthority?: Pick<
RuntimeHostAccessAuthority,
'activeSessionGrant' | 'subscribeGrantRevocations'
>,
) {
this.#hostEpoch = hostEpoch;
this.#readCanonical = readCanonical;
this.#transcriptReader = transcriptReader;
this.#sessionAccessAuthority = sessionAccessAuthority;
this.#unsubscribeGrantRevocations = sessionAccessAuthority?.subscribeGrantRevocations(
(grant) => {
if (grant.kind !== 'session_observation') return;
for (const subscriber of this.#subscriptions.values()) {
if (
subscriber.principalId === grant.principalId &&
subscriber.sessionId === grant.sessionId
) {
this.#closeSubscriber(subscriber, 'access_revoked');
}
}
},
);
}
attachConnection(
connectionId: string,
sink: SessionContinuityFrameSink,
): SessionContinuityConnection {
if (this.#closed) throw new Error('Session continuity coordinator is closed');
if (this.#connections.has(connectionId)) {
throw new Error(`Duplicate Runtime Host connection: ${connectionId}`);
}
this.#connections.set(connectionId, {
sink,
subscriptionIds: new Set(),
pendingOpenCount: 0,
closed: new AbortController(),
});
let attached = true;
return {
activate: (subscriptionId) => {
if (attached) this.#activate(connectionId, subscriptionId);
},
abort: (subscriptionId) => {
if (attached) this.#abortSubscription(connectionId, subscriptionId);
},
close: () => {
if (!attached) return;
attached = false;
this.#closeConnection(connectionId);
},
};
}
async refreshCanonical(sessionId: string, admission?: SessionAdmissionLease): Promise<void> {
this.onCatalogChanged(sessionId);
await this.#runInSessionLane(
sessionId,
async () => {
if (this.#closed) return;
const state = this.#sessions.get(sessionId);
if (!state || (state.subscribers.size === 0 && !state.terminalPublicationFence)) return;
this.#invalidateTranscriptOverlay(state);
const canonical = await this.#readCanonicalProjection(sessionId);
if (this.#closed || !canonical) return;
await this.#refreshTranscriptHighWater(sessionId, state);
const committed = this.#commitCanonical(sessionId, canonical);
if (committed.changed) this.#broadcastProjection(committed.state, committed.value);
},
admission,
);
}
/** Safe for synchronous commit hooks: this only schedules and coalesces lane work. */
enqueueCanonicalRefresh(sessionId: string): void {
if (this.#closed) return;
const pending = this.#pendingRefreshes.get(sessionId);
if (pending) {
if (pending.inFlight) pending.dirty = true;
return;
}
const refresh: PendingRefresh = { dirty: false, inFlight: false };
this.#pendingRefreshes.set(sessionId, refresh);
void this.sessionAdmission
.enqueueDetached(sessionId, async (lease) => {
refresh.inFlight = true;
await this.refreshCanonical(sessionId, lease);
if (!refresh.dirty) return;
refresh.dirty = false;
await this.refreshCanonical(sessionId, lease);
})
.then(
() => {
this.#pendingRefreshes.delete(sessionId);
if (refresh.dirty) this.enqueueCanonicalRefresh(sessionId);
},
(error) => {
this.#pendingRefreshes.delete(sessionId);
this.onPublicationFailure(error);
},
);
}
/** Coalesce process-local graph invalidations onto the root Session sequence. */
enqueueAgentGraphChanged(event: {
rootSessionId: string;
graphId: string;
reason: AgentGraphChangedReason;
}): void {
if (this.#closed) return;
const pending = this.#pendingAgentGraphChanges.get(event.rootSessionId);
if (pending) {
pending.event = { ...event };
return;
}
const change: PendingAgentGraphChange = { event: { ...event } };
this.#pendingAgentGraphChanges.set(event.rootSessionId, change);
void this.sessionAdmission
.enqueueDetached(event.rootSessionId, () => {
if (this.#pendingAgentGraphChanges.get(event.rootSessionId) !== change) return;
this.#pendingAgentGraphChanges.delete(event.rootSessionId);
if (this.#closed) return;
const state = this.#sessions.get(event.rootSessionId);
if (!state) return;
for (const subscriber of state.subscribers.values()) {
if (subscriber.principalKind === 'session_guest') continue;
const frame: AgentGraphChangedFrame = {
kind: 'subscription.agent_graph_changed',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
...change.event,
};
this.#enqueue(subscriber, frame);
}
})
.catch((error: unknown) => {
if (this.#pendingAgentGraphChanges.get(event.rootSessionId) === change) {
this.#pendingAgentGraphChanges.delete(event.rootSessionId);
}
this.onPublicationFailure(error);
});
}
/** Coalesce domain projection invalidations onto the Session subscription sequence. */
enqueueSessionDomainChanged(sessionId: string, domain: SessionProjectionDomain): void {
if (this.#closed) return;
const pending = this.#pendingSessionDomainChanges.get(sessionId);
if (pending) {
pending.domains.add(domain);
return;
}
const changes: PendingSessionDomainChanges = {
domains: new Set([domain]),
runtimeResources: new Map(),
};
this.#pendingSessionDomainChanges.set(sessionId, changes);
this.#scheduleSessionDomainChanges(sessionId, changes);
}
/** Publish one lightweight source invalidation to every active Session view that may inherit it. */
enqueueRuntimeResourceChanged(update: ShellRunUpdate): void {
if (this.#closed) return;
const resource = { sourceSessionId: update.sessionId, ref: update.result.ref };
const key = JSON.stringify([resource.sourceSessionId, resource.ref]);
for (const sessionId of this.#sessions.keys()) {
const pending = this.#pendingSessionDomainChanges.get(sessionId);
if (pending) {
pending.runtimeResources.set(key, resource);
continue;
}
const changes: PendingSessionDomainChanges = {
domains: new Set(),
runtimeResources: new Map([[key, resource]]),
};
this.#pendingSessionDomainChanges.set(sessionId, changes);
this.#scheduleSessionDomainChanges(sessionId, changes);
}
}
/** PTY congestion never consumes Session sequence numbers or its queue budget. */
async enqueueRuntimeResourcePtyData(event: {
sessionId: string;
ref: string;
sequence: number;
data: string;
}): Promise<void> {
if (this.#closed) return;
try {
const state = this.#sessions.get(event.sessionId);
if (!state) return;
for (const subscriber of state.subscribers.values()) {
if (!this.#canObserve(subscriber, event.sessionId)) {
this.#closeSubscriber(subscriber, 'access_revoked');
continue;
}
if (!subscriber.ptyInterests.has(event.ref)) continue;
const frame: SessionRuntimeResourcePtyDataFrame = {
kind: 'subscription.runtime_resource_pty_data',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sessionId: event.sessionId,
ref: event.ref,
ptySequence: event.sequence,
...(Buffer.byteLength(event.data, 'utf8') > SESSION_RUNTIME_RESOURCE_PTY_DATA_MAX_BYTES
? { data: '', reset: true as const }
: { data: event.data }),
};
if (
Buffer.byteLength(JSON.stringify(frame), 'utf8') <= SESSION_SUBSCRIPTION_FRAME_MAX_BYTES
) {
this.#enqueuePty(subscriber, frame);
}
}
} catch (error) {
this.onPublicationFailure(error);
}
}
#scheduleSessionDomainChanges(sessionId: string, changes: PendingSessionDomainChanges): void {
void this.sessionAdmission
.enqueueDetached(sessionId, () => {
if (this.#pendingSessionDomainChanges.get(sessionId) !== changes) return;
this.#pendingSessionDomainChanges.delete(sessionId);
if (this.#closed) return;
const state = this.#sessions.get(sessionId);
if (!state) return;
const frames: SessionDomainChange[] = [...changes.domains].map((domain) => ({
sessionId,
domain,
}));
const runtimeResources = [...changes.runtimeResources.values()];
for (
let offset = 0;
offset < runtimeResources.length;
offset += SESSION_RUNTIME_RESOURCE_CHANGES_MAX
) {
frames.push({
sessionId,
domain: 'runtime_resource',
resources: runtimeResources.slice(
offset,
offset + SESSION_RUNTIME_RESOURCE_CHANGES_MAX,
),
});
}
for (const change of frames) {
for (const subscriber of state.subscribers.values()) {
const projected =
change.domain === 'runtime_resource' && subscriber.principalKind === 'session_guest'
? {
...change,
resources: change.resources.filter(
(resource) => resource.sourceSessionId === subscriber.sessionId,
),
}
: change;
if (projected.domain === 'runtime_resource' && projected.resources.length === 0) {
continue;
}
const frame: SessionDomainChangedFrame = {
kind: 'subscription.session_domain_changed',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
...projected,
};
this.#enqueue(subscriber, frame);
}
}
})
.catch((error: unknown) => {
if (this.#pendingSessionDomainChanges.get(sessionId) === changes) {
this.#pendingSessionDomainChanges.delete(sessionId);
}
this.onPublicationFailure(error);
});
}
async holdTerminalPublication(
sessionId: string,
turnId: string,
runId: string,
admission?: SessionAdmissionLease,
): Promise<void> {
await this.#runInSessionLane(
sessionId,
async () => {
if (this.#closed) throw new Error('Session continuity coordinator is closed');
const state = this.#sessions.get(sessionId);
const existing = state?.terminalPublicationFence;
if (existing) {
if (existing.turnId === turnId && existing.runId === runId) return;
throw new Error('Session already has a different terminal publication fence');
}
const canonical = await this.#readCanonicalProjection(sessionId);
if (this.#closed) throw new Error('Session continuity coordinator is closed');
if (!canonical) throw new Error('Cannot fence a missing Session projection');
const rootTurn = requirePublicationFenceIdentity(canonical, sessionId, { turnId, runId });
if (isTerminalTurn(rootTurn)) {
throw new Error(
'Terminal publication fence identity does not match a non-terminal canonical Turn',
);
}
const committed = this.#commitCanonical(sessionId, canonical);
committed.state.terminalPublicationFence = { turnId, runId };
if (committed.changed) this.#broadcastProjection(committed.state, committed.value);
},
admission,
);
}
async publishTerminalProjection(
sessionId: string,
turnId: string,
runId: string,
admission?: SessionAdmissionLease,
): Promise<void> {
await this.#runInSessionLane(
sessionId,
async () => {
if (this.#closed) throw new Error('Session continuity coordinator is closed');
const state = this.#sessions.get(sessionId);
const fence = state?.terminalPublicationFence;
if (!state || !fence || fence.turnId !== turnId || fence.runId !== runId) {
throw new Error('Terminal publication does not own the Session continuity fence');
}
const canonical = await this.#readCanonicalProjection(sessionId);
if (this.#closed) throw new Error('Session continuity coordinator is closed');
if (!canonical) {
throw new Error('Canonical Session projection is not terminal for the fenced Turn');
}
const rootTurn = requirePublicationFenceIdentity(canonical, sessionId, fence);
if (!isTerminalTurn(rootTurn)) {
throw new Error('Canonical Session projection is not terminal for the fenced Turn');
}
if (isDeepStrictEqual(state.canonical, canonical)) {
throw new Error('Fenced terminal projection was already published');
}
await this.#refreshTranscriptHighWater(sessionId, state);
const nextRevision = state.revision + 1;
const snapshot = createSessionContinuitySnapshot(canonical, nextRevision);
this.#invalidateTranscriptOverlay(state);
state.canonical = canonical;
state.revision = nextRevision;
delete state.terminalPublicationFence;
state.assistantStreams.clear();
state.toolResultPreviews.clear();
this.#broadcastProjection(state, snapshot);
if (state.subscribers.size === 0) {
this.#invalidateTranscriptOverlay(state);
this.#sessions.delete(sessionId);
}
},
admission,
);
}
async acceptRuntimeEvent(
sessionId: string,
runId: string,
event: RuntimeSessionForwardedEvent,
): Promise<void> {
if (
(event.type === 'text_delta' || event.type === 'thinking_delta') &&
event.text.length === 0
) {
return;
}
if (
(event.type === 'tool_output_delta' && event.chunk.length === 0) ||
(event.type === 'tool_progress' &&
(typeof event.chunk === 'string' ? event.chunk : event.chunk.text).length === 0)
) {
return;
}
await this.sessionAdmission.run(sessionId, async () => {
let state = this.#sessions.get(sessionId);
if (!state) {
const canonical = await this.#readCanonicalProjection(sessionId);
if (!canonical) throw new Error('Runtime event belongs to a missing Session');
state = this.#commitCanonical(sessionId, canonical).state;
}
this.#invalidateTranscriptOverlay(state);
const rootTurn = state.canonical.rootTurn;
if (
!rootTurn ||
rootTurn.sessionId !== sessionId ||
rootTurn.turnId !== event.turnId ||
rootTurn.runId !== runId ||
isTerminalTurn(rootTurn) ||
(event.type === 'tool_output_delta' && event.sessionId !== sessionId)
) {
throw new Error('Runtime event does not belong to the canonical active root Turn');
}
if (event.type === 'provider_retry') {
this.#publishCanonical(state, withProviderRetry(state.canonical, event));
return;
}
this.#publishCanonical(state, withoutProviderRetry(state.canonical));
if (event.type === 'text_delta' || event.type === 'thinking_delta') {
const kind: SessionAssistantDelta['kind'] =
event.type === 'text_delta' ? 'text' : 'thinking';
const prefixKey = assistantStreamKey(kind, event.messageId);
const current = state.assistantStreams.get(prefixKey);
const startOffset = current?.text.length ?? 0;
state.assistantStreams.set(prefixKey, {
turnId: event.turnId,
messageId: event.messageId,
kind,
text: (current?.text ?? '') + event.text,
});
for (const subscriber of state.subscribers.values()) {
this.#enqueueAssistantDelta(subscriber, sessionId, runId, event, kind, startOffset);
}
return;
}
if (event.type === 'text_complete' || event.type === 'thinking_complete') {
if (event.type === 'thinking_complete') {
const prefixKey = assistantStreamKey('thinking', event.messageId);
const current = state.assistantStreams.get(prefixKey) ?? {
kind: 'thinking' as const,
turnId: event.turnId,
messageId: event.messageId,
text: '',
};
current.completedParts = [...(current.completedParts ?? []), event.text];
state.assistantStreams.set(prefixKey, current);
return;
}
const thinkingKey = assistantStreamKey('thinking', event.messageId);
const thinking = state.assistantStreams.get(thinkingKey);
if (thinking) {
const finalThinking = thinking.completedParts?.join('') ?? thinking.text;
for (const subscriber of state.subscribers.values()) {
this.#enqueueAssistantCompletion(
subscriber,
sessionId,
runId,
thinking,
'thinking',
finalThinking,
);
}
state.assistantStreams.delete(thinkingKey);
}
const textKey = assistantStreamKey('text', event.messageId);
const text = state.assistantStreams.get(textKey);
if (text || event.text.length > 0 || event.interrupted) {
const current =
text ??
({
kind: 'text',
turnId: event.turnId,
messageId: event.messageId,
text: '',
} satisfies ActiveAssistantStream);
for (const subscriber of state.subscribers.values()) {
this.#enqueueAssistantCompletion(
subscriber,
sessionId,
runId,
current,
'text',
event.text,
event.interrupted,
);
}
state.assistantStreams.delete(textKey);
}
return;
}
if (event.type === 'tool_result_preview') {
state.toolResultPreviews.set(event.toolUseId, event);
} else if (event.type === 'tool_result') {
state.toolResultPreviews.delete(event.toolUseId);
}
for (const subscriber of state.subscribers.values()) {
const frame: SessionEventFrame = {
kind: 'subscription.session_event',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
sessionId,
runId,
event: projectSessionEvent(
event,
sessionId,
subscriber.principalKind === 'session_guest',
),
};
this.#enqueue(subscriber, frame);
}
});
}
async retireSessions(
sessionIds: readonly string[],
admission: SessionAdmissionLease,
): Promise<void> {
for (const sessionId of new Set(sessionIds)) {
await this.#runInSessionLane(
sessionId,
() => {
const state = this.#sessions.get(sessionId);
if (!state) return;
for (const subscriber of state.subscribers.values()) {
this.#enqueueSessionRemoved(subscriber);
}
this.#invalidateTranscriptOverlay(state);
this.#sessions.delete(sessionId);
},
admission,
);
}
}
close(): void {
if (this.#closed) return;
this.#closed = true;
this.#unsubscribeGrantRevocations?.();
this.#cancelTranscriptOverlayPreparationWaiters();
for (const connectionId of [...this.#connections.keys()]) this.#closeConnection(connectionId);
for (const state of this.#sessions.values()) this.#invalidateTranscriptOverlay(state);
this.#sessions.clear();
this.#subscriptions.clear();
this.#pendingRefreshes.clear();
this.#pendingAgentGraphChanges.clear();
this.#pendingSessionDomainChanges.clear();
}
async #open(
context: ConnectionContext,
input: SubscriptionOpenInput,
): Promise<
| { ok: true; value: SubscriptionOpenResult }
| {
ok: false;
code:
| 'not_found'
| 'operation_conflict'
| 'operation_unavailable'
| 'persistence_failed'
| 'transcript_preparing';
message: string;
}
> {
const connectionId = context.connectionId;
const identity = connectionIdentity(context);
const sessionId = input.sessionId;
const connection = this.#connections.get(connectionId);
if (!connection) throw new Error('Runtime Host connection is not attached to continuity');
if (!this.#canObserve(identity, sessionId)) {
return { ok: false, code: 'not_found', message: 'Session was not found' };
}
if (
connection.subscriptionIds.size + connection.pendingOpenCount >=
MAX_CONNECTION_SUBSCRIPTIONS
) {
return {
ok: false,
code: 'operation_conflict',
message: 'Runtime Host connection subscription limit reached',
};
}
connection.pendingOpenCount += 1;
let preparationPermit: TranscriptOverlayPreparationPermit | undefined;
let retryAfterCapacity = false;
try {
for (;;) {
try {
return await this.sessionAdmission.run(sessionId, async () => {
if (this.#connections.get(connectionId) !== connection) {
throw new Error('Runtime Host connection closed during subscription open');
}
const canonical = await this.#readCanonicalProjection(sessionId);
if (this.#connections.get(connectionId) !== connection) {
throw new Error('Runtime Host connection closed during subscription open');
}
if (!canonical) {
return {
ok: false as const,
code: 'not_found' as const,
message: 'Session was not found',
};
}
const committed = this.#commitCanonical(sessionId, canonical);
if (committed.changed) {
this.#invalidateTranscriptOverlay(committed.state);
this.#broadcastProjection(committed.state, committed.value);
}
if (this.#connections.get(connectionId) !== connection) {
this.#scheduleInactiveStateCleanup(sessionId, committed.state);
throw new Error('Runtime Host connection closed during subscription open');
}
const subscriptionId = randomUUID();
const activeAssistantStreams = [...committed.state.assistantStreams.values()].map(
({ kind, turnId, messageId }) => ({ kind, turnId, messageId }),
);
let transcript: SubscriberTranscriptState | undefined;
let retainedTranscriptOverlay: RetainedTranscriptOverlay | undefined;
let cachedTranscriptOverlay: CachedTranscriptOverlay | undefined;
let transcriptSubscriberInstalled = false;
let transcriptBootstrap: SubscriptionOpenResult['transcript'] = null;
try {
if (input.transcript.kind === 'tail') {
if (!this.#transcriptReader) {
return {
ok: false as const,
code: 'operation_unavailable' as const,
message: 'Session transcript is unavailable',
};
}
try {
const throughSequence =
await this.#transcriptReader.readDurableHighWater(sessionId);
cachedTranscriptOverlay = this.#prepareTranscriptOverlay(
committed.state,
sessionId,
throughSequence,
preparationPermit,
);
cachedTranscriptOverlay.pendingConsumers += 1;
retainedTranscriptOverlay = await waitForConnectionOpen(
cachedTranscriptOverlay.prepared,
connection.closed.signal,
);
const snapshot = projectSessionSnapshot(committed.value, identity.principalKind);
const created = await createSessionTranscriptBootstrap({
reader: this.#transcriptReader,
sessionId,
subscriptionId,
throughSequence,
rootTurn: committed.state.canonical.rootTurn,
activeAssistantStreams: committed.state.assistantStreams.values(),
maxBytes: input.transcript.maxBytes,
preparedOverlayMessages: retainedTranscriptOverlay.messages,
projection: identity.principalKind === 'session_guest' ? 'shared' : 'owner',
maxEncodedBytes: subscriptionOpenTranscriptBudget({
hostEpoch: this.#hostEpoch,
subscriptionId,
nextSequence: 1,
snapshot,
activeAssistantStreams,
transcript: null,
}),
});
transcript = created.state;
transcriptBootstrap = created.bootstrap;
} catch (error) {
if (error instanceof TranscriptOverlayPreparationRequired) throw error;
return {
ok: false as const,
code:
error instanceof TranscriptOverlayCapacityError
? ('operation_unavailable' as const)
: ('persistence_failed' as const),
message:
error instanceof TranscriptOverlayCapacityError
? 'Runtime Host transcript overlay capacity reached'
: 'Session transcript is unavailable',
};
}
}
if (this.#connections.get(connectionId) !== connection) {
this.#scheduleInactiveStateCleanup(sessionId, committed.state);
throw new Error('Runtime Host connection closed during subscription open');
}
const openValue: SubscriptionOpenResult = {
hostEpoch: this.#hostEpoch,
subscriptionId,
nextSequence: 1,
snapshot: projectSessionSnapshot(committed.value, identity.principalKind),
activeAssistantStreams,
transcript: transcriptBootstrap,
};
if (
Buffer.byteLength(JSON.stringify(openValue), 'utf8') >
SUBSCRIPTION_OPEN_RESULT_MAX_BYTES
) {
return {
ok: false as const,
code: 'operation_unavailable' as const,
message: 'Session subscription state exceeds the transport limit',
};
}
if (!this.#canObserve(identity, sessionId)) {
return {
ok: false as const,
code: 'not_found' as const,
message: 'Session was not found',
};
}
const subscriber: Subscriber = {
connectionId,
principalId: identity.principalId,
principalKind: identity.principalKind,
sessionId,
subscriptionId,
sink: connection.sink,
phase: 'open',
activated: false,
nextSequence: 1,
lastFlushedSequence: 0,
queue: [],
ptyQueue: [],
ptyInterests: new Set(),
ptyQueuedBytes: 0,
ptyPumping: false,
queuedBytes: 0,
pumping: false,
terminalQueued: false,
...(transcript ? { transcript } : {}),
...(retainedTranscriptOverlay ? { retainedTranscriptOverlay } : {}),
};
if (subscriber.retainedTranscriptOverlay)
this.#retainTranscriptOverlay(subscriber.retainedTranscriptOverlay);
committed.state.subscribers.set(subscriptionId, subscriber);
this.#subscriptions.set(subscriptionId, subscriber);
connection.subscriptionIds.add(subscriptionId);
transcriptSubscriberInstalled = subscriber.retainedTranscriptOverlay !== undefined;
// Client expects the first delivered frame at nextSequence from the open
// result. Capture that before enqueueing retained previews — each
// #enqueue advances nextSequence.
const firstSequence = subscriber.nextSequence;
// Seed retained live previews so a mid-turn rejoin still has Open facts.
const rootTurn = committed.state.canonical.rootTurn;
if (rootTurn && !isTerminalTurn(rootTurn)) {
for (const preview of committed.state.toolResultPreviews.values()) {
if (preview.turnId !== rootTurn.turnId) continue;
const frame: SessionEventFrame = {
kind: 'subscription.session_event',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
sessionId,
runId: rootTurn.runId,
event: projectSessionEvent(
preview,
sessionId,
subscriber.principalKind === 'session_guest',
),
};
this.#enqueue(subscriber, frame);
}
}
return {
ok: true as const,
value: { ...openValue, nextSequence: firstSequence },
};
} finally {
if (cachedTranscriptOverlay) cachedTranscriptOverlay.pendingConsumers -= 1;
if (
cachedTranscriptOverlay &&
!transcriptSubscriberInstalled &&
cachedTranscriptOverlay.pendingConsumers === 0 &&
!this.#hasTranscriptOverlayConsumer(committed.state)
) {
this.#invalidateTranscriptOverlay(committed.state, cachedTranscriptOverlay);
}
}
});
} catch (error) {
if (!(error instanceof TranscriptOverlayPreparationRequired)) throw error;
if (retryAfterCapacity) {
return {
ok: false as const,
code: 'operation_unavailable' as const,
message: 'Runtime Host transcript overlay capacity reached',
};
}
try {
preparationPermit = await this.#acquireTranscriptOverlayPreparation(connection);
} catch (acquireError) {
if (acquireError instanceof TranscriptOverlayCapacityError) {
retryAfterCapacity = true;
continue;
}
return {
ok: false as const,
code: 'persistence_failed' as const,
message: 'Session transcript is unavailable',
};
}
}
}
} finally {
preparationPermit?.release();
connection.pendingOpenCount -= 1;
}
}
async #readTranscriptPage(
connectionId: string,
input: SessionTranscriptPageInput,
): Promise<OperationOutcome<'session.transcript.page'>> {
const subscriber = this.#ownedSubscriber(connectionId, input.subscriptionId);
if (!subscriber) {
return {
ok: false,
error: { code: 'not_found', message: 'Session subscription was not found' },
};
}
if (!this.#transcriptReader || !subscriber.transcript) {
return {
ok: false,
error: { code: 'operation_unavailable', message: 'Session transcript is unavailable' },
};
}
const connection = this.#connections.get(connectionId);
if (!connection || !this.#canObserve(subscriber, subscriber.sessionId)) {
this.#closeSubscriber(subscriber, 'access_revoked');
return transcriptSubscriptionNotFound();
}
const transcript = subscriber.transcript;
return this.sessionAdmission.run(subscriber.sessionId, async () => {
if (
this.#ownedSubscriber(connectionId, input.subscriptionId) !== subscriber ||
this.#connections.get(connectionId) !== connection ||
!this.#canObserve(subscriber, subscriber.sessionId)
) {
return transcriptSubscriptionNotFound();
}
try {
const page = await readSessionTranscriptPage({
reader: this.#transcriptReader!,
state: transcript,
request: input,
});
if (
this.#ownedSubscriber(connectionId, input.subscriptionId) !== subscriber ||
this.#connections.get(connectionId) !== connection ||
!this.#canObserve(subscriber, subscriber.sessionId)
) {
return transcriptSubscriptionNotFound();
}
return { ok: true, result: page };
} catch (error) {
if (error instanceof TranscriptPageRequestError) {
return {
ok: false,
error: { code: 'invalid_request', message: error.message },
};
}
return {
ok: false,
error: { code: 'persistence_failed', message: 'Session transcript is unavailable' },
};
}
});
}
#prepareTranscriptOverlay(
state: SessionProjectionState,
sessionId: string,
throughSequence: number | null,
permit: TranscriptOverlayPreparationPermit | undefined,
): CachedTranscriptOverlay {
if (!this.#transcriptReader) throw new Error('Session transcript is unavailable');
const cached = state.transcriptOverlay;
if (cached?.throughSequence === throughSequence) return cached;
if (cached) this.#invalidateTranscriptOverlay(state, cached);
if (!state.canonical.rootTurn || isTerminalTurn(state.canonical.rootTurn)) {
const prepared = Promise.resolve(this.#registerTranscriptOverlay([]));
const entry = {
throughSequence,
prepared,
pendingConsumers: 0,
cancelPreparation: () => {},
};
state.transcriptOverlay = entry;
return entry;
}
if (!permit) throw new TranscriptOverlayPreparationRequired();
const release = permit.take();
const prepared = (async () => {
try {
if (permit.waiter.cancelled) {
throw new Error('Session transcript overlay preparation was cancelled');
}
const messages = await prepareSessionTranscriptOverlay({
reader: this.#transcriptReader!,
sessionId,
throughSequence,
rootTurn: state.canonical.rootTurn,
activeAssistantStreams: state.assistantStreams.values(),
});
return this.#registerTranscriptOverlay(messages);
} finally {
release();
}
})();
const entry = {
throughSequence,
prepared,
pendingConsumers: 0,
cancelPreparation: () => this.#cancelTranscriptOverlayPreparation(permit.waiter),
};
state.transcriptOverlay = entry;
void prepared.catch(() => {
if (state.transcriptOverlay === entry) state.transcriptOverlay = undefined;
});
return entry;
}
async #acquireTranscriptOverlayPreparation(
connection: ConnectionState,
): Promise<TranscriptOverlayPreparationPermit> {
const ticket = this.#queueTranscriptOverlayPreparation();
let release: () => void;
try {
release = await waitForConnectionOpen(ticket.ready, connection.closed.signal);
} catch (error) {
this.#cancelTranscriptOverlayPreparation(ticket.waiter);
throw error;
}
let available = true;
return {
waiter: ticket.waiter,
take: () => {
if (!available) {
throw new Error('Session transcript overlay preparation permit is unavailable');
}
available = false;
return release;
},
release: () => {
if (!available) return;
available = false;
release();
},
};
}
#queueTranscriptOverlayPreparation(): {
readonly waiter: TranscriptOverlayPreparationWaiter;
readonly ready: Promise<() => void>;
} {
if (
this.#transcriptOverlayPreparationWaiters.length >= MAX_TRANSCRIPT_OVERLAY_PREPARATION_WAITERS
) {
throw new TranscriptOverlayCapacityError(
'Runtime Host transcript overlay preparation queue reached its limit',
);
}
let resolve!: (release: () => void) => void;
let reject!: (error: Error) => void;
const ready = new Promise<() => void>((resolveReady, rejectReady) => {
resolve = resolveReady;
reject = rejectReady;
});
const waiter: TranscriptOverlayPreparationWaiter = {
cancelled: false,
granted: false,
resolve,
reject,
};
this.#transcriptOverlayPreparationWaiters.push(waiter);
this.#drainTranscriptOverlayPreparationWaiters();
return { waiter, ready };
}
#releaseTranscriptOverlayPreparation(): void {
this.#preparingTranscriptOverlayBytes -= MAX_TRANSCRIPT_OVERLAY_PREPARATION_BYTES;
this.#drainTranscriptOverlayPreparationWaiters();
}
#drainTranscriptOverlayPreparationWaiters(): void {
if (this.#closed) return;
while (this.#transcriptOverlayPreparationWaiters.length > 0) {
const waiter = this.#transcriptOverlayPreparationWaiters[0]!;
if (waiter.cancelled) {
this.#transcriptOverlayPreparationWaiters.shift();
continue;
}
if (
this.#retainedTranscriptOverlayBytes + MAX_TRANSCRIPT_OVERLAY_PREPARATION_BYTES >
MAX_RETAINED_TRANSCRIPT_OVERLAY_BYTES
) {
if (this.#preparingTranscriptOverlayBytes > 0) return;
this.#transcriptOverlayPreparationWaiters.shift();
waiter.cancelled = true;
waiter.reject(
new TranscriptOverlayCapacityError('Runtime Host transcript overlay capacity reached'),
);
continue;
}
if (
this.#retainedTranscriptOverlayBytes +
this.#preparingTranscriptOverlayBytes +
MAX_TRANSCRIPT_OVERLAY_PREPARATION_BYTES >
MAX_RETAINED_TRANSCRIPT_OVERLAY_BYTES
) {
return;
}
this.#transcriptOverlayPreparationWaiters.shift();
waiter.granted = true;
this.#preparingTranscriptOverlayBytes += MAX_TRANSCRIPT_OVERLAY_PREPARATION_BYTES;
let released = false;
waiter.resolve(() => {
if (released) return;
released = true;
this.#releaseTranscriptOverlayPreparation();
});
}
}
#cancelTranscriptOverlayPreparation(waiter: TranscriptOverlayPreparationWaiter): void {
if (waiter.cancelled) return;
waiter.cancelled = true;
if (!waiter.granted) {
const index = this.#transcriptOverlayPreparationWaiters.indexOf(waiter);
if (index >= 0) this.#transcriptOverlayPreparationWaiters.splice(index, 1);
waiter.reject(new Error('Session transcript overlay preparation was cancelled'));
this.#drainTranscriptOverlayPreparationWaiters();
}
}
#cancelTranscriptOverlayPreparationWaiters(): void {
for (const waiter of this.#transcriptOverlayPreparationWaiters.splice(0)) {
waiter.cancelled = true;
waiter.reject(new Error('Session continuity coordinator is closed'));
}
}
#registerTranscriptOverlay(messages: readonly Buffer[]): RetainedTranscriptOverlay {
const bytes = messages.reduce((total, message) => total + message.byteLength, 0);
if (this.#retainedTranscriptOverlayBytes + bytes > MAX_RETAINED_TRANSCRIPT_OVERLAY_BYTES) {
throw new TranscriptOverlayCapacityError('Runtime Host transcript overlay capacity reached');
}
const retained = { messages, bytes, references: 1 };
this.#retainedTranscriptOverlays.set(messages, retained);
this.#retainedTranscriptOverlayBytes += bytes;
return retained;
}
#retainTranscriptOverlay(retained: RetainedTranscriptOverlay): void {
if (
retained.references < 1 ||
this.#retainedTranscriptOverlays.get(retained.messages) !== retained
) {
throw new Error('Session transcript overlay is no longer retained');
}
retained.references += 1;
}
#releaseTranscriptOverlay(retained: RetainedTranscriptOverlay): void {
if (retained.references < 1) return;
retained.references -= 1;
if (retained.references > 0) return;
this.#retainedTranscriptOverlays.delete(retained.messages);
this.#retainedTranscriptOverlayBytes -= retained.bytes;
this.#drainTranscriptOverlayPreparationWaiters();
}
#invalidateTranscriptOverlay(
state: SessionProjectionState,
expected?: CachedTranscriptOverlay,
): void {
const cached = state.transcriptOverlay;
if (!cached || (expected && cached !== expected)) return;
state.transcriptOverlay = undefined;
cached.cancelPreparation();
void cached.prepared.then(
(retained) => this.#releaseTranscriptOverlay(retained),
() => undefined,
);
}
#hasTranscriptOverlayConsumer(state: SessionProjectionState): boolean {
return (
(state.transcriptOverlay?.pendingConsumers ?? 0) > 0 ||
[...state.subscribers.values()].some(
(subscriber) => subscriber.retainedTranscriptOverlay !== undefined,
)
);
}
async #refreshTranscriptHighWater(
sessionId: string,
state: SessionProjectionState,
): Promise<void> {
if (!this.#transcriptReader || state.subscribers.size === 0) return;
if (![...state.subscribers.values()].some((subscriber) => subscriber.transcript)) return;
const throughSequence = await this.#transcriptReader.readDurableHighWater(sessionId);
for (const subscriber of state.subscribers.values()) {
if (
!subscriber.transcript ||
!updateSubscriberTranscriptHighWater(subscriber.transcript, throughSequence) ||
throughSequence === null
) {
continue;
}
const frame: SessionTranscriptAdvancedFrame = {
kind: 'subscription.transcript_advanced',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
sessionId,
throughSequence,
};
this.#enqueue(subscriber, frame);
}
}
#activate(connectionId: string, subscriptionId: string): void {
const subscriber = this.#ownedSubscriber(connectionId, subscriptionId);
if (!subscriber || subscriber.activated || subscriber.phase === 'closed') return;
subscriber.activated = true;
this.#pump(subscriber);
this.#pumpPty(subscriber);
}
#abortSubscription(connectionId: string, subscriptionId: string): void {
const subscriber = this.#ownedSubscriber(connectionId, subscriptionId);
if (subscriber) this.#removeSubscriber(subscriber);
}
#closeSubscription(connectionId: string, subscriptionId: string): boolean {
const connection = this.#connections.get(connectionId);
if (!connection) return false;
const subscriber = this.#subscriptions.get(subscriptionId);
if (!subscriber) return true;
if (
subscriber.connectionId !== connectionId ||
!connection.subscriptionIds.has(subscriptionId)
) {
return false;
}
this.#removeSubscriber(subscriber);
return true;
}
#closeConnection(connectionId: string): void {
const connection = this.#connections.get(connectionId);
if (!connection) return;
connection.closed.abort(new Error('Runtime Host connection closed during subscription open'));
for (const subscriptionId of [...connection.subscriptionIds]) {
const subscriber = this.#ownedSubscriber(connectionId, subscriptionId);
if (subscriber) this.#removeSubscriber(subscriber);
}
this.#connections.delete(connectionId);
}
#enqueue(subscriber: Subscriber, frame: OrderedSubscriptionFrame): void {
if (subscriber.phase !== 'open' || subscriber.terminalQueued) return;
let encodedBytes: number;
try {
encodedBytes = encodeProtocolMessage(frame).byteLength;
} catch {
this.#evictSlowSubscriber(subscriber);
return;
}
const terminalBytes = terminalFrameByteBudget(subscriber, this.#hostEpoch);
// Assistant text/thinking floods arrive far faster than the
// one-awaited-send-at-a-time flush can drain them, and the queue budget
// exists to bound memory, not to force eviction. Fold a delta into the
// queued tail when it continues the same stream: projectors apply deltas
// by absolute startOffset, so a merged frame carries byte-identical
// content, and the absorbed frame never spends a sequence, keeping later
// frames contiguous. The in-flight head frame is never touched.
const tail = subscriber.queue[subscriber.queue.length - 1];
if (tail && (!subscriber.pumping || subscriber.queue.length > 1)) {
const mergedText = mergeableAssistantDeltaText(tail.frame, frame);
if (mergedText !== undefined && tail.frame.kind === 'subscription.session_delta') {
const merged: OrderedSubscriptionFrame = {
...tail.frame,
delta: { ...tail.frame.delta, text: mergedText },
};
const mergedEncodedBytes = encodeProtocolMessage(merged).byteLength;
// Merging must preserve the wire invariants the split path
// guarantees per frame: the decoder rejects a delta text beyond
// SESSION_LIVE_DELTA_MAX_BYTES and any subscription frame beyond
// SESSION_SUBSCRIPTION_FRAME_MAX_BYTES, so an oversized merge would
// break the very subscription coalescing tries to preserve. Keep
// the next delta as its own frame instead.
if (
Buffer.byteLength(mergedText, 'utf8') <= SESSION_LIVE_DELTA_MAX_BYTES &&
mergedEncodedBytes <= SESSION_SUBSCRIPTION_FRAME_MAX_BYTES &&
subscriber.queuedBytes - tail.encodedBytes + mergedEncodedBytes + terminalBytes <=
MAX_SUBSCRIBER_QUEUED_BYTES
) {
tail.frame = merged;
subscriber.queuedBytes += mergedEncodedBytes - tail.encodedBytes;
tail.encodedBytes = mergedEncodedBytes;
return;
}
}
}
if (
subscriber.queue.length >= MAX_SUBSCRIBER_QUEUED_FRAMES - 1 ||
subscriber.queuedBytes + encodedBytes + terminalBytes > MAX_SUBSCRIBER_QUEUED_BYTES
) {
this.#evictSlowSubscriber(subscriber);
return;
}
subscriber.queue.push({ frame, encodedBytes });
subscriber.queuedBytes += encodedBytes;
subscriber.nextSequence += 1;
if (subscriber.activated) this.#pump(subscriber);
}
#evictSlowSubscriber(subscriber: Subscriber): void {
this.#closeSubscriber(subscriber, 'slow_consumer');
}
#enqueuePty(subscriber: Subscriber, frame: SessionRuntimeResourcePtyDataFrame): void {
if (subscriber.phase !== 'open' || subscriber.terminalQueued) return;
let encodedBytes = encodeProtocolMessage(frame).byteLength;
if (subscriber.ptyQueue.length >= 8 || subscriber.ptyQueuedBytes + encodedBytes > 128 * 1024) {
// Reset is session-wide for terminal consumers, so one marker covers
// every omitted resource without an unbounded per-resource dirty set.
const inFlight = subscriber.ptyPumping ? subscriber.ptyQueue[0] : undefined;
subscriber.ptyQueue = inFlight ? [inFlight] : [];
subscriber.ptyQueuedBytes = inFlight?.encodedBytes ?? 0;
frame = { ...frame, data: '', reset: true };
encodedBytes = encodeProtocolMessage(frame).byteLength;
}
subscriber.ptyQueue.push({ frame, encodedBytes });
subscriber.ptyQueuedBytes += encodedBytes;
this.#pumpPty(subscriber);
}
#pumpPty(subscriber: Subscriber): void {
if (subscriber.ptyPumping || !subscriber.activated || subscriber.phase !== 'open') return;
const queued = subscriber.ptyQueue[0];
if (!queued) return;
subscriber.ptyPumping = true;
void Promise.resolve()
.then(() => {
if (subscriber.phase !== 'open') return;
return subscriber.sink.send(queued.frame);
})
.then(
() => {
subscriber.ptyPumping = false;
if (subscriber.ptyQueue[0] === queued) {
subscriber.ptyQueue.shift();
subscriber.ptyQueuedBytes -= queued.encodedBytes;
}
this.#pumpPty(subscriber);
},
() => this.#removeSubscriber(subscriber),
);
}
#closeSubscriber(subscriber: Subscriber, reason: 'slow_consumer' | 'access_revoked'): void {
if (subscriber.phase !== 'open') return;
subscriber.phase = 'closing';
const inFlight = subscriber.pumping ? subscriber.queue[0] : undefined;
subscriber.queue = [];
subscriber.queuedBytes = 0;
subscriber.nextSequence = (inFlight?.frame.sequence ?? subscriber.lastFlushedSequence) + 1;
const frame: SubscriptionFrame = {
kind: 'subscription.closed',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
reason,
};
subscriber.nextSequence += 1;
subscriber.terminalQueued = true;
const encodedBytes = encodeProtocolMessage(frame).byteLength;
if (inFlight) {
subscriber.queue.push(inFlight);
subscriber.queuedBytes += inFlight.encodedBytes;
}
subscriber.queue.push({ frame, encodedBytes });
subscriber.queuedBytes += encodedBytes;
if (subscriber.activated) this.#pump(subscriber);
}
#canObserve(
identity: { readonly principalId: string; readonly principalKind: Subscriber['principalKind'] },
sessionId: string,
): boolean {
return (
identity.principalKind !== 'session_guest' ||
this.#sessionAccessAuthority?.activeSessionGrant(
identity.principalId,
sessionId,
'session_observation',
) !== undefined
);
}
#enqueueAssistantDelta(
subscriber: Subscriber,
sessionId: string,
runId: string,
event: Extract<RuntimeSessionForwardedEvent, { type: 'text_delta' | 'thinking_delta' }>,
kind: SessionAssistantDelta['kind'],
startOffset: number,
): void {
this.#enqueueAssistantText(subscriber, sessionId, runId, event, kind, startOffset, event.text);
}
#enqueueAssistantCompletion(
subscriber: Subscriber,
sessionId: string,
runId: string,
current: Pick<ActiveAssistantStream, 'turnId' | 'messageId' | 'text'>,
kind: SessionAssistantDelta['kind'],
finalText: string,
interrupted?: true,
): void {
const extendsPrefix = finalText.startsWith(current.text);
const suffix = extendsPrefix ? finalText.slice(current.text.length) : finalText;
if (suffix) {
this.#enqueueAssistantText(
subscriber,
sessionId,
runId,
current,
kind,
extendsPrefix ? current.text.length : 0,
suffix,
!extendsPrefix,
);
}
if (subscriber.phase !== 'open') return;
this.#enqueue(subscriber, {
kind: 'subscription.session_delta',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
sessionId,
delta: {
kind,
turnId: current.turnId,
runId,
messageId: current.messageId,
startOffset: finalText.length,
text: '',
...(!extendsPrefix && finalText.length === 0 ? { reset: true as const } : {}),
complete: true,
...(interrupted ? { interrupted: true } : {}),
},
});
}
#enqueueAssistantText(
subscriber: Subscriber,
sessionId: string,
runId: string,
event: Pick<ActiveAssistantStream, 'turnId' | 'messageId' | 'text'>,
kind: SessionAssistantDelta['kind'],
startOffset: number,
text: string,
reset = false,
): void {
let chunk = '';
let rawBytes = 0;
let wireBytes = 0;
let emittedCharacters = 0;
const frame = (text: string): SessionDeltaFrame => ({
kind: 'subscription.session_delta',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
sessionId,
delta: {
kind,
turnId: event.turnId,
runId,
messageId: event.messageId,
startOffset: startOffset + emittedCharacters,
text,
...(reset && emittedCharacters === 0 ? { reset: true } : {}),
},
});
let wireLimit = wireTextByteLimit(frame(''));
for (const character of text) {
const rawCharacterBytes = Buffer.byteLength(character, 'utf8');
const wireCharacterBytes = jsonStringContentBytes(character);
if (
chunk.length > 0 &&
(rawBytes + rawCharacterBytes > SESSION_LIVE_DELTA_MAX_BYTES ||
wireBytes + wireCharacterBytes > wireLimit)
) {
this.#enqueue(subscriber, frame(chunk));
emittedCharacters += chunk.length;
if (subscriber.phase !== 'open') return;
chunk = '';
rawBytes = 0;
wireBytes = 0;
wireLimit = wireTextByteLimit(frame(''));
}
if (rawCharacterBytes > SESSION_LIVE_DELTA_MAX_BYTES || wireCharacterBytes > wireLimit) {
throw new Error('Session delta character exceeds the wire frame budget');
}
chunk += character;
rawBytes += rawCharacterBytes;
wireBytes += wireCharacterBytes;
}
if (chunk.length > 0 && subscriber.phase === 'open') this.#enqueue(subscriber, frame(chunk));
}
#enqueueSessionRemoved(subscriber: Subscriber): void {
if (subscriber.phase !== 'open' || subscriber.terminalQueued) return;
const frame: SubscriptionFrame = {
kind: 'subscription.closed',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
reason: 'session_removed',
};
const encodedBytes = encodeProtocolMessage(frame).byteLength;
if (
subscriber.queue.length >= MAX_SUBSCRIBER_QUEUED_FRAMES ||
subscriber.queuedBytes + encodedBytes > MAX_SUBSCRIBER_QUEUED_BYTES
) {
throw new Error('Session removal terminal headroom was not preserved');
}
subscriber.queue.push({ frame, encodedBytes });
subscriber.queuedBytes += encodedBytes;
subscriber.nextSequence += 1;
subscriber.terminalQueued = true;
if (subscriber.activated) this.#pump(subscriber);
}
#pump(subscriber: Subscriber): void {
if (subscriber.pumping || !subscriber.activated || subscriber.phase === 'closed') return;
const queued = subscriber.queue[0];
if (!queued) return;
subscriber.pumping = true;
let flushed: Promise<void>;
try {
flushed = subscriber.sink.send(queued.frame);
} catch {
this.#removeSubscriber(subscriber);
return;
}
void flushed.then(
() => {
subscriber.pumping = false;
if (subscriber.phase === 'closed') return;
if (subscriber.queue[0] === queued) {
subscriber.queue.shift();
subscriber.queuedBytes -= queued.encodedBytes;
}
subscriber.lastFlushedSequence = queued.frame.sequence;
if (queued.frame.kind === 'subscription.closed') {
this.#removeSubscriber(subscriber);
return;
}
this.#pump(subscriber);
},
() => this.#removeSubscriber(subscriber),
);
}
#removeSubscriber(subscriber: Subscriber): void {
if (subscriber.phase === 'closed') return;
subscriber.phase = 'closed';
subscriber.queue = [];
subscriber.queuedBytes = 0;
subscriber.ptyQueue = [];
subscriber.ptyQueuedBytes = 0;
const state = this.#sessions.get(subscriber.sessionId);
const removed = state?.subscribers.delete(subscriber.subscriptionId);
this.#subscriptions.delete(subscriber.subscriptionId);
this.#connections
.get(subscriber.connectionId)
?.subscriptionIds.delete(subscriber.subscriptionId);
this.#releaseSubscriberTranscriptOverlay(subscriber);
if (!this.#closed && state && removed) {
if (!this.#hasTranscriptOverlayConsumer(state)) this.#invalidateTranscriptOverlay(state);
if (state.subscribers.size === 0) {
this.#scheduleInactiveStateCleanup(subscriber.sessionId, state);
}
}
}
#releaseSubscriberTranscriptOverlay(subscriber: Subscriber): void {
if (subscriber.transcript) subscriber.transcript.overlayMessages = undefined;
const retained = subscriber.retainedTranscriptOverlay;
if (!retained) return;
subscriber.retainedTranscriptOverlay = undefined;
this.#releaseTranscriptOverlay(retained);
const state = this.#sessions.get(subscriber.sessionId);
if (state && !this.#hasTranscriptOverlayConsumer(state)) {
this.#invalidateTranscriptOverlay(state);
}
}
#ownedSubscriber(connectionId: string, subscriptionId: string): Subscriber | undefined {
const connection = this.#connections.get(connectionId);
if (!connection?.subscriptionIds.has(subscriptionId)) return;
const subscriber = this.#subscriptions.get(subscriptionId);
if (subscriber?.connectionId === connectionId) return subscriber;
}
#scheduleInactiveStateCleanup(sessionId: string, state: SessionProjectionState): void {
if (this.#closed) return;
void this.sessionAdmission.enqueueDetached(sessionId, () => {
if (
this.#sessions.get(sessionId) === state &&
state.subscribers.size === 0 &&
!state.terminalPublicationFence &&
(!state.canonical.rootTurn || isTerminalTurn(state.canonical.rootTurn))
) {
this.#invalidateTranscriptOverlay(state);
this.#sessions.delete(sessionId);
}
});
}
async #readCanonicalProjection(sessionId: string): Promise<CanonicalSessionProjection | null> {
const canonical = await this.#readCanonical(sessionId);
return canonical ? immutableClone(canonical) : null;
}
#commitCanonical(
sessionId: string,
canonical: CanonicalSessionProjection,
): { changed: boolean; state: SessionProjectionState; value: SessionContinuitySnapshot } {
let state = this.#sessions.get(sessionId);
if (state?.terminalPublicationFence) {
const rootTurn = requirePublicationFenceIdentity(
canonical,
sessionId,
state.terminalPublicationFence,
);
if (isTerminalTurn(rootTurn)) {
return {
changed: false,
state,
value: createSessionContinuitySnapshot(state.canonical, state.revision),
};
}
}
if (!state) {
const value = createSessionContinuitySnapshot(canonical, 1);
state = {
canonical,
revision: 1,
subscribers: new Map(),
assistantStreams: new Map(),
toolResultPreviews: new Map(),
};
this.#sessions.set(sessionId, state);
return { changed: true, state, value };
}
canonical = preserveProviderRetry(state.canonical, canonical);
const changed = !isDeepStrictEqual(state.canonical, canonical);
if (changed) {
if (state.canonical.rootTurn?.runId !== canonical.rootTurn?.runId) {
state.assistantStreams.clear();
state.toolResultPreviews.clear();
}
const nextRevision = state.revision + 1;
const value = createSessionContinuitySnapshot(canonical, nextRevision);
state.canonical = canonical;
state.revision = nextRevision;
return { changed, state, value };
}
return {
changed,
state,
value: createSessionContinuitySnapshot(state.canonical, state.revision),
};
}
#publishCanonical(state: SessionProjectionState, canonical: CanonicalSessionProjection): void {
if (isDeepStrictEqual(state.canonical, canonical)) return;
const nextRevision = state.revision + 1;
const snapshot = createSessionContinuitySnapshot(canonical, nextRevision);
state.canonical = immutableClone(canonical);
state.revision = nextRevision;
this.#broadcastProjection(state, snapshot);
}
#broadcastProjection(state: SessionProjectionState, snapshot: SessionContinuitySnapshot): void {
for (const subscriber of state.subscribers.values()) {
this.#enqueue(subscriber, {
kind: 'subscription.session_projection',
hostEpoch: this.#hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence,
snapshot: projectSessionSnapshot(snapshot, subscriber.principalKind),
});
}
}
#runInSessionLane<T>(
sessionId: string,
operation: () => Promise<T> | T,
admission?: SessionAdmissionLease,
): Promise<T> {
return admission
? this.sessionAdmission.runAdmitted(sessionId, admission, operation)
: this.sessionAdmission.run(sessionId, operation);
}
}
function slowConsumerFrameBytes(subscriber: Subscriber, hostEpoch: string): number {
return encodeProtocolMessage({
kind: 'subscription.closed',
hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence + 1,
reason: 'slow_consumer',
}).byteLength;
}
function assistantStreamKey(kind: SessionAssistantDelta['kind'], messageId: string): string {
return `${kind}\0${messageId}`;
}
function transcriptSubscriptionNotFound(): OperationOutcome<'session.transcript.page'> {
return {
ok: false,
error: { code: 'not_found', message: 'Session subscription was not found' },
};
}
function terminalFrameByteBudget(subscriber: Subscriber, hostEpoch: string): number {
return Math.max(
slowConsumerFrameBytes(subscriber, hostEpoch),
encodeProtocolMessage({
kind: 'subscription.closed',
hostEpoch,
subscriptionId: subscriber.subscriptionId,
sequence: subscriber.nextSequence + 1,
reason: 'session_removed',
}).byteLength,
);
}
/**
* Returns the concatenated text when `next` continues `tail`'s assistant
* stream contiguously, making the two frames safe to ship as one. Reset and
* completion frames never merge: a reset must land on its own boundary and a
* completion closes the stream.
*/
function mergeableAssistantDeltaText(
tail: SubscriptionFrame,
next: SubscriptionFrame,
): string | undefined {
if (tail.kind !== 'subscription.session_delta' || next.kind !== 'subscription.session_delta')
return undefined;
const a = tail.delta;
const b = next.delta;
if (
a.kind !== b.kind ||
a.turnId !== b.turnId ||
a.runId !== b.runId ||
a.messageId !== b.messageId
)
return undefined;
if (a.complete === true || b.complete === true || a.reset === true || b.reset === true)
return undefined;
if (a.startOffset + a.text.length !== b.startOffset) return undefined;
return a.text + b.text;
}
function immutableClone<T>(value: T): T {
return deepFreeze(structuredClone(value));
}
function deepFreeze<T>(value: T): T {
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
for (const child of Object.values(value)) deepFreeze(child);
return Object.freeze(value);
}
function requirePublicationFenceIdentity(
canonical: CanonicalSessionProjection,
sessionId: string,
fence: TerminalPublicationFence,
): TurnSnapshot {
const rootTurn = canonical.rootTurn;
if (
canonical.session.sessionId !== sessionId ||
!rootTurn ||
rootTurn.sessionId !== sessionId ||
rootTurn.turnId !== fence.turnId ||
rootTurn.runId !== fence.runId
) {
throw new Error('Canonical Session projection identity does not match its publication fence');
}
return rootTurn;
}
function isTerminalTurn(turn: TurnSnapshot): boolean {
return turn.status === 'completed' || turn.status === 'failed' || turn.status === 'cancelled';
}
function isLiveTurn(turn: TurnSnapshot): turn is LiveTurnSnapshot {
return !isTerminalTurn(turn);
}
function withProviderRetry(
canonical: CanonicalSessionProjection,
event: Extract<SessionEvent, { type: 'provider_retry' }>,
): CanonicalSessionProjection {
const rootTurn = canonical.rootTurn;
if (!rootTurn || !isLiveTurn(rootTurn)) return canonical;
const providerRetry: TurnProviderRetry =
event.phase === 'scheduled'
? {
phase: 'scheduled',
attempt: event.attempt,
maxAttempts: event.maxAttempts,
delayMs: event.delayMs,
ts: event.ts,
reason: event.reason,
}
: {
phase: 'started',
attempt: event.attempt,
maxAttempts: event.maxAttempts,
reason: event.reason,
};
return { ...canonical, rootTurn: { ...rootTurn, providerRetry } };
}
function withoutProviderRetry(canonical: CanonicalSessionProjection): CanonicalSessionProjection {
const rootTurn = canonical.rootTurn;
if (!rootTurn || !isLiveTurn(rootTurn) || rootTurn.providerRetry === undefined) {
return canonical;
}
const { providerRetry: _providerRetry, ...cleared } = rootTurn;
return { ...canonical, rootTurn: cleared };
}
function preserveProviderRetry(
current: CanonicalSessionProjection,
next: CanonicalSessionProjection,
): CanonicalSessionProjection {
const currentTurn = current.rootTurn;
const nextTurn = next.rootTurn;
if (
!currentTurn ||
!nextTurn ||
!isLiveTurn(currentTurn) ||
!isLiveTurn(nextTurn) ||
currentTurn.runId !== nextTurn.runId ||
currentTurn.turnId !== nextTurn.turnId ||
currentTurn.providerRetry === undefined
) {
return next;
}
if (nextTurn.providerRetry !== undefined) return next;
return { ...next, rootTurn: { ...nextTurn, providerRetry: currentTurn.providerRetry } };
}
function wireTextByteLimit(frame: SessionDeltaFrame): number {
return RUNTIME_HOST_MAX_MESSAGE_BYTES - encodeProtocolMessage(frame).byteLength;
}
function subscriptionOpenTranscriptBudget(result: SubscriptionOpenResult): number {
const withoutTranscriptBytes = Buffer.byteLength(JSON.stringify(result), 'utf8');
// Replacing the JSON literal null with the bootstrap object preserves every
// other byte in the result.
return SUBSCRIPTION_OPEN_RESULT_MAX_BYTES - withoutTranscriptBytes + 4;
}
function jsonStringContentBytes(value: string): number {
const encoded = JSON.stringify(value);
return Buffer.byteLength(encoded.slice(1, -1), 'utf8');
}
function connectionIdentity(context: ConnectionContext): {
readonly principalId: string;
readonly principalKind: Subscriber['principalKind'];
} {
if (!context.principalKind) {
throw new Error('Runtime Host connection has no authenticated principal kind');
}
return { principalId: context.principal, principalKind: context.principalKind };
}
function projectSessionSnapshot(
snapshot: SessionContinuitySnapshot,
principalKind: Subscriber['principalKind'],
): SessionContinuitySnapshot {
if (principalKind !== 'session_guest') return snapshot;
const projectEntry = <T extends { readonly content: import('@maka/core/events').MessageContent }>(
entry: T,
): T => ({
...entry,
content: projectSharedSessionMessageContent(entry.content, snapshot.session.sessionId),
});
return {
...snapshot,
queue: {
...snapshot.queue,
steering: snapshot.queue.steering.map(projectEntry),
followup: snapshot.queue.followup.map(projectEntry),
},
};
}
function projectSessionEvent(
event: Exclude<
RuntimeSessionForwardedEvent,
{
type:
| 'text_delta'
| 'thinking_delta'
| 'text_complete'
| 'thinking_complete'
| 'provider_retry';
}
>,
sessionId: string,
shared = false,
): SessionToolEvent | SessionSteeringEvent {
if (event.type === 'steering_message') {
// The durable steering echo: forwarded verbatim so subscribers render the
// interjection in place instead of depending on observing the transient
// in-flight queue state.
return {
type: 'steering_message',
id: event.id,
turnId: event.turnId,
ts: event.ts,
messageId: event.messageId,
content: shared
? projectSharedSessionMessageContent(event.content, sessionId)
: structuredClone(event.content),
};
}
const identity = {
id: event.id,
turnId: event.turnId,
ts: event.ts,
toolUseId: event.toolUseId,
};
switch (event.type) {
case 'tool_start': {
const shellRunRef = toolStartShellRunRef(event);
return {
type: event.type,
...identity,
toolName: boundedUtf8(event.toolName, SESSION_TOOL_NAME_MAX_BYTES),
...(event.operationId === undefined ? {} : { operationId: event.operationId }),
...(event.activityKind === undefined ? {} : { activityKind: event.activityKind }),
...(event.displayName === undefined
? {}
: { displayName: boundedUtf8(event.displayName, SESSION_TOOL_NAME_MAX_BYTES) }),
...(event.intent === undefined
? {}
: { intent: boundedUtf8(event.intent, SESSION_TOOL_INTENT_MAX_BYTES) }),
// A correlated hidden-shell poll publishes only its correlation ref:
// the frame is deliberately minimal (#3569), so no args preview rides
// along. Every other live tool start names itself for compact rows.
...(shellRunRef ? {} : projectArgsPreviewForWire(event.toolName, event.args)),
...(event.stepId === undefined ? {} : { stepId: event.stepId }),
...(shellRunRef ? { shellRunRef } : {}),
};
}
case 'tool_output_delta':
return {
type: event.type,
...identity,
seq: event.seq,
stream: event.stream,
chunk: event.chunk,
redacted: event.redacted,
createdAt: event.createdAt,
};
case 'tool_progress':
return {
type: event.type,
...identity,
chunk: boundedUtf8(
typeof event.chunk === 'string' ? event.chunk : event.chunk.text,
SESSION_LIVE_DELTA_MAX_BYTES,
),
};
case 'tool_result':
return {
type: event.type,
...identity,
...(event.operationId === undefined ? {} : { operationId: event.operationId }),
status: event.isError ? 'errored' : 'completed',
...(event.isError && event.content.kind === 'text' && event.content.sandboxFailure
? { sandboxFailureReason: event.content.sandboxFailure.reason }
: {}),
...(event.durationMs === undefined ? {} : { durationMs: event.durationMs }),
};
case 'tool_result_preview':
return {
type: event.type,
...identity,
isError: event.isError,
content: event.content,
};
}
}
/**
* Build the wire `argsPreview` spread for a live `tool_start`. The preview is
* computed and bounded in `@maka/core`; the extra byte check here is the
* wire-budget guard so a formatter change cannot silently bloat frames.
*/
function projectArgsPreviewForWire(toolName: string, args: unknown): { argsPreview?: unknown } {
const preview = projectToolArgsPreview(toolName, args);
if (preview === undefined) return {};
if (Buffer.byteLength(JSON.stringify(preview), 'utf8') > SESSION_TOOL_ARGS_PREVIEW_MAX_BYTES) {
return {};
}
return { argsPreview: preview };
}
function boundedUtf8(value: string, maxBytes: number): string {
if (Buffer.byteLength(value, 'utf8') <= maxBytes) return value;
let bounded = '';
let bytes = 0;
for (const character of value) {
const characterBytes = Buffer.byteLength(character, 'utf8');
if (bytes + characterBytes > maxBytes) break;
bounded += character;
bytes += characterBytes;
}
return bounded;
}
function toolStartShellRunRef(
event: Extract<RuntimeSessionForwardedEvent, { type: 'tool_start' }>,
): string | undefined {
if (event.toolName !== 'Read' && event.toolName !== 'StopBackgroundTask') return undefined;
const ref =
event.args !== null && typeof event.args === 'object'
? event.toolName === 'Read'
? (event.args as { path?: unknown }).path
: (event.args as { ref?: unknown }).ref
: undefined;
if (typeof ref !== 'string') return undefined;
try {
return decodeRuntimeResourceRef(resolveReadInput({ path: ref }).path);
} catch {
return undefined;
}
}
function waitForConnectionOpen<T>(task: Promise<T>, closed: AbortSignal): Promise<T> {
// A race against a connection-lifetime Promise retains every winning overlay
// until disconnect. Remove the close listener as soon as this wait finishes.
return new Promise<T>((resolve, reject) => {
const onClose = () => reject(closed.reason);
if (closed.aborted) onClose();
else closed.addEventListener('abort', onClose, { once: true });
void task.then(
(value) => {
closed.removeEventListener('abort', onClose);
resolve(value);
},
(error: unknown) => {
closed.removeEventListener('abort', onClose);
reject(error);
},
);
});
}