Skip to content
68 changes: 68 additions & 0 deletions apps/server/src/ws.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,74 @@ it.live.each(["thread", "shell"] as const)(
}).pipe(Effect.scoped, Effect.provide(ReplayTestLayer)),
);

it.effect("coalesces streaming replay before synchronization and the live tail", () =>
Effect.gen(function* () {
const store = yield* OrchestrationEventStore.OrchestrationEventStore;
const sql = yield* SqlClient.SqlClient;
const threadId = ThreadId.make("thread:streaming-replay");
const now = "2026-10-27T00:00:00.000Z";
const insert = (version: number, text: string, streaming: boolean) => sql<{
sequence: number;
}>`
INSERT INTO orchestration_events (
event_id, aggregate_kind, stream_id, stream_version, event_type,
occurred_at, actor_kind, payload_json, metadata_json, application_event_version
) VALUES (
${`streaming-replay:${version}`}, 'thread', ${threadId}, ${version}, 'message.updated',
${now}, 'server', ${JSON.stringify({
createdBy: "agent",
creationSource: "web",
id: "message:streaming-replay",
threadId,
runId: null,
nodeId: null,
role: "assistant",
text,
attachments: [],
streaming,
createdAt: now,
updatedAt: now,
})}, '{}', 2
) RETURNING sequence
`;
yield* insert(0, "H", true);
yield* insert(1, "He", true);
const [latest] = yield* insert(2, "Hello", true);
const [completed] = yield* insert(3, "Hello!", false);
const items = yield* subscribeOrchestrationV2Thread({
threadId,
afterSequence: 0,
requestCompletionMarker: true,
}).pipe(
Effect.flatMap(Stream.runCollect),
Effect.provideService(ThreadManagement.ThreadManagementService, {
ensureLegacyTranscript: () => Effect.void,
streamStoredEventsFrom: () =>
Stream.unwrap(
insert(4, "Next", false).pipe(
Effect.map(() =>
store.readAgentEvents({ threadId, afterSequence: completed!.sequence }),
),
),
),
} as unknown as ThreadManagement.ThreadManagementService["Service"]),
);
assert.deepEqual(
items.map((item) =>
item.kind === "event"
? [item.sequence, item.event.type === "message.updated" ? item.event.payload.text : null]
: [item.kind],
),
[
[latest!.sequence, "Hello"],
[completed!.sequence, "Hello!"],
["synchronized"],
[completed!.sequence + 1, "Next"],
],
);
}).pipe(Effect.scoped, Effect.provide(ReplayTestLayer)),
);

it.effect("shell snapshots omit idle children and older settled threads only on request", () =>
Effect.gen(function* () {
const shell = (
Expand Down
9 changes: 7 additions & 2 deletions apps/server/src/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ import {
type ShellApplicationEvent,
} from "./orchestration-v2/ShellStream.ts";
import { ORCHESTRATION_V2_PROJECTION_SCHEMA_VERSION } from "./orchestration-v2/ProjectionStore.ts";
import { coalesceThreadLiveStream } from "./orchestration-v2/ThreadLiveEventCoalescer.ts";
import {
coalesceLiveUpdatedEvents,
coalesceThreadLiveStream,
} from "./orchestration-v2/ThreadLiveEventCoalescer.ts";
import {
buildBoundedThreadStreamSnapshot,
decideThreadResume,
Expand Down Expand Up @@ -947,7 +950,9 @@ export const subscribeOrchestrationV2Thread = Effect.fn("ws.orchestrationV2.subs
// Retained creation events may describe deleted threads; replace replay only if a snapshot exists.
if (shell !== null) return yield* snapshotThenLive();
}
const replay = yield* loadReplayThrough(input.afterSequence, highWater);
const replay = coalesceLiveUpdatedEvents(
yield* loadReplayThrough(input.afterSequence, highWater),
);
const plan = decideThreadResume({
afterSequence: input.afterSequence,
highWater,
Expand Down
128 changes: 124 additions & 4 deletions packages/client-runtime/src/authorization/layer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { AuthStandardClientScopes, EnvironmentId } from "@t3tools/contracts";
import {
AuthStandardClientScopes,
EnvironmentId,
ORCHESTRATION_PROTOCOL_VERSION,
} from "@t3tools/contracts";
import { type RelayEnvironmentConnectResponse } from "@t3tools/contracts/relay";
import { describe, expect, it } from "@effect/vitest";
import * as Deferred from "effect/Deferred";
Expand All @@ -15,6 +19,11 @@ import * as RpcHttp from "../rpc/http.ts";
import * as ClientCapabilities from "../platform/capabilities.ts";
import * as RemoteEnvironmentAuthorization from "./service.ts";
import * as TokenStore from "./tokenStore.ts";
import { BearerConnectionCredential, BearerConnectionProfile } from "../connection/catalog.ts";
import * as ConnectionCredentialStore from "../connection/credentialStore.ts";
import { BearerConnectionTarget } from "../connection/model.ts";
import * as ConnectionProfileStore from "../connection/profileStore.ts";
import * as ConnectionResolver from "../connection/resolver.ts";

const ENVIRONMENT_ID = EnvironmentId.make("environment-1");
const ENDPOINT = {
Expand Down Expand Up @@ -186,7 +195,7 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* (
resetTokenCache: Effect.void,
});
const layer = RemoteEnvironmentAuthorization.layer.pipe(
Layer.provide(
Layer.provideMerge(
Layer.mergeAll(
RpcHttp.layerRemoteHttpClient(fetch.fetchFn),
Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer),
Expand Down Expand Up @@ -225,13 +234,124 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* (
};
});

const prepareDirectBearer = Effect.fn("TestRemoteAuthorization.prepareDirectBearer")(function* (
harness: Effect.Success<ReturnType<typeof makeHarness>>,
times: number,
) {
const target = new BearerConnectionTarget({
environmentId: ENVIRONMENT_ID,
label: DESCRIPTOR.label,
connectionId: "direct-1",
});
const entry = {
target,
profile: Option.some(
new BearerConnectionProfile({
connectionId: target.connectionId,
environmentId: ENVIRONMENT_ID,
label: target.label,
httpBaseUrl: ENDPOINT.httpBaseUrl,
wsBaseUrl: ENDPOINT.wsBaseUrl,
}),
),
enabled: true,
};
const unused = () => Effect.die("unused");
const layer = ConnectionResolver.layer.pipe(
Layer.provide(
Layer.mergeAll(
harness.layer,
Layer.succeed(ConnectionProfileStore.ConnectionProfileStore, {
get: unused,
put: unused,
remove: unused,
}),
Layer.succeed(ConnectionCredentialStore.ConnectionCredentialStore, {
get: () => Effect.succeedSome(new BearerConnectionCredential({ token: "bearer-token" })),
put: unused,
remove: unused,
}),
Layer.succeed(ClientCapabilities.PrimaryEnvironmentAuth, {
bearerToken: Effect.succeedNone,
}),
Layer.succeed(ClientCapabilities.SshEnvironmentGateway, {
provision: unused,
prepare: unused,
disconnect: unused,
}),
),
),
);
const resolver = yield* ConnectionResolver.ConnectionResolver.pipe(Effect.provide(layer));
return yield* Effect.forEach(Array.from({ length: times }), () =>
resolver.prepare(entry).pipe(Effect.result),
);
});

describe("RemoteEnvironmentAuthorization", () => {
it.effect("reuses a validated bearer descriptor while issuing fresh websocket tickets", () =>
it.effect("prepares a cold direct bearer connection with one descriptor request", () =>
Effect.gen(function* () {
const harness = yield* makeHarness({
responses: [
Response.json({
...DESCRIPTOR,
orchestrationProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION,
}),
websocketTicket("cold-ticket"),
],
});

const [prepared] = yield* prepareDirectBearer(harness, 1);

expect(prepared).toMatchObject({
_tag: "Success",
success: { socketUrl: expect.stringContaining("wsTicket=cold-ticket") },
});
expect(harness.fetch.calls.map(([url]) => new URL(String(url)).pathname)).toEqual([
"/.well-known/t3/environment",
"/api/auth/websocket-ticket/rpc",
]);
}),
);

it.effect("blocks a warm direct bearer prepare when the environment changes identity", () =>
Effect.gen(function* () {
const harness = yield* makeHarness({
responses: [
Response.json({
...DESCRIPTOR,
orchestrationProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION,
}),
websocketTicket("first-ticket"),
Response.json({
...DESCRIPTOR,
environmentId: "another-environment",
orchestrationProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION,
}),
],
});

const [first, second] = yield* prepareDirectBearer(harness, 2);

expect(first?._tag).toBe("Success");
expect(second).toMatchObject({
_tag: "Failure",
failure: { _tag: "ConnectionBlockedError", reason: "configuration" },
});
expect(
harness.fetch.calls.filter(([url]) =>
String(url).endsWith("/api/auth/websocket-ticket/rpc"),
),
).toHaveLength(1);
}),
);
it.effect("validates a fresh bearer descriptor while issuing fresh websocket tickets", () =>
Effect.gen(function* () {
const harness = yield* makeHarness({
responses: [
Response.json(DESCRIPTOR),
websocketTicket("first-ticket"),
Response.json(DESCRIPTOR),
websocketTicket("second-ticket"),
],
});
Expand All @@ -253,7 +373,7 @@ describe("RemoteEnvironmentAuthorization", () => {
expect(second.socketUrl).toContain("wsTicket=second-ticket");
expect(
harness.fetch.calls.filter(([url]) => String(url).endsWith("/.well-known/t3/environment")),
).toHaveLength(1);
).toHaveLength(2);
expect(
harness.fetch.calls.filter(([url]) =>
String(url).endsWith("/api/auth/websocket-ticket/rpc"),
Expand Down
54 changes: 14 additions & 40 deletions packages/client-runtime/src/authorization/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as Ref from "effect/Ref";
import * as Result from "effect/Result";
import * as Scope from "effect/Scope";
import * as Semaphore from "effect/Semaphore";
Expand All @@ -50,6 +49,7 @@ export interface AuthorizedRemoteEnvironment {
readonly httpBaseUrl: string;
readonly socketUrl: string;
readonly httpAuthorization: PreparedHttpAuthorization;
readonly descriptor?: ExecutionEnvironmentDescriptor;
}

export interface AuthorizedRemoteHttpEnvironment {
Expand Down Expand Up @@ -86,7 +86,6 @@ export class RemoteEnvironmentAuthorization extends Context.Service<
>()("@t3tools/client-runtime/authorization/service/RemoteEnvironmentAuthorization") {}

const CACHED_ENDPOINT_SOCKET_TIMEOUT_MS = 3_000;
const BEARER_DESCRIPTOR_CACHE_TTL_MS = 10_000;
const DPOP_AUTHORIZATION_TIMEOUT_MS = 30_000;

function mapDpopSocketError(error: RemoteEnvironmentAuthError | ConnectionAttemptError) {
Expand Down Expand Up @@ -127,16 +126,6 @@ export const make = Effect.gen(function* () {
readonly token: Effect.Effect<TokenStore.RemoteDpopAccessToken, ConnectionAttemptError>;
}
>();
const bearerDescriptors = yield* Ref.make<
ReadonlyMap<
EnvironmentId,
{
readonly httpBaseUrl: string;
readonly descriptor: ExecutionEnvironmentDescriptor;
readonly validatedAtEpochMs: number;
}
>
>(new Map());

/**
* A saved LAN address can belong to another machine on a different network.
Expand All @@ -147,33 +136,15 @@ export const make = Effect.gen(function* () {
readonly httpBaseUrl: string;
readonly connectionMethod: ClientConnectionMethod;
}) {
const now = yield* Clock.currentTimeMillis;
const cachedDescriptor = (yield* Ref.get(bearerDescriptors)).get(input.expectedEnvironmentId);
const canReuseDescriptor =
cachedDescriptor?.httpBaseUrl === input.httpBaseUrl &&
cachedDescriptor.validatedAtEpochMs + BEARER_DESCRIPTOR_CACHE_TTL_MS > now;
const descriptor = canReuseDescriptor
? cachedDescriptor.descriptor
: yield* fetchDescriptor(input.httpBaseUrl, input.connectionMethod).pipe(
Effect.provideService(HttpClient.HttpClient, httpClient),
);
const descriptor = yield* fetchDescriptor(input.httpBaseUrl, input.connectionMethod).pipe(
Effect.provideService(HttpClient.HttpClient, httpClient),
);
if (descriptor.environmentId !== input.expectedEnvironmentId) {
return yield* environmentMismatchError({
expected: input.expectedEnvironmentId,
actual: descriptor.environmentId,
});
}
if (!canReuseDescriptor) {
yield* Ref.update(bearerDescriptors, (current) => {
const next = new Map(current);
next.set(input.expectedEnvironmentId, {
httpBaseUrl: input.httpBaseUrl,
descriptor,
validatedAtEpochMs: now,
});
return next;
});
}
return descriptor;
},
);
Expand Down Expand Up @@ -204,6 +175,7 @@ export const make = Effect.gen(function* () {
label: descriptor.label,
httpBaseUrl: input.httpBaseUrl,
socketUrl,
descriptor,
httpAuthorization: {
_tag: "Bearer" as const,
token: input.bearerToken,
Expand Down Expand Up @@ -492,16 +464,18 @@ export const make = Effect.gen(function* () {
input: Parameters<RemoteEnvironmentAuthorization["Service"]["authorizeDpop"]>[0],
) {
const endpoint = input.directEndpoint;
if (endpoint !== undefined) {
yield* verifyDirectEndpoint({
expectedEnvironmentId: input.expectedEnvironmentId,
httpBaseUrl: endpoint.httpBaseUrl,
connectionMethod: "direct",
});
}
const descriptor =
endpoint === undefined
? undefined
: yield* verifyDirectEndpoint({
expectedEnvironmentId: input.expectedEnvironmentId,
httpBaseUrl: endpoint.httpBaseUrl,
connectionMethod: "direct",
});
const authorized = (token: TokenStore.RemoteDpopAccessToken, socketUrl: string) => ({
...httpAuthorization(token, endpoint?.httpBaseUrl),
socketUrl,
...(descriptor === undefined ? {} : { descriptor }),
});
let selected = yield* getDpopToken(input);
if (selected.fromCache) {
Expand Down
Loading
Loading