authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-25 18:01:07-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
log5a755ada28fa8da9d435f2e1f14697e83f4aa6db
tree4839da3b79b19f57e1d3cde38994131cbcb79199
parentb211d059d2b88e3ffce38b5e9b59cdc7b2e0259a

nits


3 files changed, 33 insertions(+), 29 deletions(-)

apps/server/src/wsServer.ts+30-27
......@@ -183,23 +183,22 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
183183 });
184184 }
185185
186 const onTerminalEvent = (event: TerminalEvent) =>
187 Effect.gen(function* () {
188 const push: WsPush = {
189 type: "push",
190 channel: WS_CHANNELS.terminalEvent,
191 data: event,
192 };
193 const message = yield* Schema.encodeEffect(Schema.fromJsonString(WsPush))(push);
194 let recipients = 0;
195 for (const client of yield* Ref.get(clients)) {
196 if (client.readyState === client.OPEN) {
197 client.send(message);
198 recipients += 1;
199 }
186 const onTerminalEvent = Effect.fnUntraced(function* (event: TerminalEvent) {
187 const push: WsPush = {
188 type: "push",
189 channel: WS_CHANNELS.terminalEvent,
190 data: event,
191 };
192 const message = yield* Schema.encodeEffect(Schema.fromJsonString(WsPush))(push);
193 let recipients = 0;
194 for (const client of yield* Ref.get(clients)) {
195 if (client.readyState === client.OPEN) {
196 client.send(message);
197 recipients += 1;
200198 }
201 logOutgoingPush(push, recipients);
202 }).pipe(Effect.runPromise);
199 }
200 logOutgoingPush(push, recipients);
201 });
203202
204203 // HTTP server — serves static files or redirects to Vite dev server
205204 const httpServer = http.createServer((req, res) => {
......@@ -289,9 +288,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
289288 const { openInEditor } = yield* Open;
290289
291290 const subscriptionsScope = yield* Scope.make("sequential");
292 yield* Effect.addFinalizer(() =>
293 Scope.close(subscriptionsScope, Exit.void).pipe(Effect.orElseSucceed(() => undefined)),
294 );
291 yield* Effect.addFinalizer(() => Scope.close(subscriptionsScope, Exit.void));
295292
296293 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>
297294 Effect.gen(function* () {
......@@ -312,7 +309,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
312309 }),
313310 ).pipe(Effect.forkIn(subscriptionsScope));
314311
315 yield* orchestrationReactor.start.pipe(Scope.provide(subscriptionsScope));
312 yield* Scope.provide(orchestrationReactor.start, subscriptionsScope);
316313
317314 if (autoBootstrapProjectFromCwd) {
318315 const snapshot = yield* projectionReadModelQuery.getSnapshot();
......@@ -332,7 +329,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
332329 }
333330 }
334331
335 const routeRequest = Effect.fn(function* (request: WebSocketRequest) {
332 const routeRequest = Effect.fnUntraced(function* (request: WebSocketRequest) {
336333 switch (request.body._tag) {
337334 case ORCHESTRATION_WS_METHODS.getSnapshot:
338335 return yield* projectionReadModelQuery.getSnapshot();
......@@ -573,18 +570,24 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
573570 ),
574571 );
575572
576 const unsubscribeTerminalEvents = yield* terminalManager.subscribe(onTerminalEvent);
573 const unsubscribeTerminalEvents = yield* terminalManager.subscribe(
574 (event) => void Effect.runPromise(onTerminalEvent(event)),
575 );
577576 yield* Effect.addFinalizer(() =>
578 Effect.sync(() => {
579 unsubscribeTerminalEvents();
580 }),
577 Effect.all([Effect.sync(() => unsubscribeTerminalEvents()), terminalManager.dispose()]),
581578 );
582 yield* Effect.addFinalizer(terminalManager.dispose);
583579
584580 yield* NodeHttpServer.make(() => httpServer, listenOptions);
585581
586582 yield* Effect.addFinalizer(() =>
587 Effect.all([closeAllClients, closeWebSocketServer.pipe(Effect.orElseSucceed(() => undefined))]),
583 Effect.all([
584 closeAllClients,
585 closeWebSocketServer.pipe(
586 Effect.catch((error) =>
587 Effect.logWarning("failed to close web socket server", { cause: error }),
588 ),
589 ),
590 ]),
588591 );
589592
590593 return httpServer;
apps/web/src/session-logic.test.ts+2-2
......@@ -69,7 +69,7 @@ describe("derivePendingApprovals", () => {
6969});
7070
7171describe("deriveWorkLogEntries", () => {
72 it("returns entries in chronological order", () => {
72 it("omits tool started entries and keeps completed entries", () => {
7373 const activities: OrchestrationThreadActivity[] = [
7474 makeActivity({
7575 id: "tool-complete",
......@@ -86,7 +86,7 @@ describe("deriveWorkLogEntries", () => {
8686 ];
8787
8888 const entries = deriveWorkLogEntries(activities, undefined);
89 expect(entries.map((entry) => entry.id)).toEqual(["tool-start", "tool-complete"]);
89 expect(entries.map((entry) => entry.id)).toEqual(["tool-complete"]);
9090 });
9191
9292 it("filters by turn id when provided", () => {
apps/web/src/session-logic.ts+1
......@@ -127,6 +127,7 @@ export function deriveWorkLogEntries(
127127 );
128128 return ordered
129129 .filter((activity) => (latestTurnId ? activity.turnId === latestTurnId : true))
130 .filter((activity) => activity.kind !== "tool.started")
130131 .filter((activity) => activity.summary !== "Checkpoint captured")
131132 .map((activity) => {
132133 const payload =