From 5fbf7a8fa9bc5cc80bec9f27b122f31114b06b3b Mon Sep 17 00:00:00 2001 From: Jack Brown Date: Fri, 17 Apr 2026 16:36:05 -0700 Subject: [PATCH 1/2] Drain artifact watcher before closing WS on end_session_message Previously ws.close() fired immediately on end_session_message and the 3s chokidar drain ran in the close handler. Late artifact_upload_request messages produced during the drain were silently dropped because send() is gated on readyState === OPEN. Move the drain into a memoized helper invoked before close() on the orderly path; the close handler still drains for unexpected disconnects. --- packages/runtimeuse/src/session.ts | 41 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/runtimeuse/src/session.ts b/packages/runtimeuse/src/session.ts index d0e0fa2..e4397ae 100644 --- a/packages/runtimeuse/src/session.ts +++ b/packages/runtimeuse/src/session.ts @@ -36,6 +36,7 @@ export class WebSocketSession { private requestInFlight = false; private secrets: string[] = []; private logger: Logger; + private drainPromise: Promise | null = null; constructor(ws: WebSocket, config: SessionConfig) { this.ws = ws; @@ -68,22 +69,7 @@ export class WebSocketSession { } this.logger.log("WebSocket connection closed"); this.currentAbortController?.abort(); - - // Give chokidar time to observe files the agent wrote right before - // the session ended. Without this, late `add` events would not fire - // before we stop the watcher, and those artifacts would be lost. - const delayMs = this.config.postInvocationDelayMs ?? 3_000; - if (this.artifactManager && delayMs > 0) { - this.logger.log(`Waiting ${delayMs}ms for artifact watcher to drain...`); - await sleep(delayMs); - } - await this.artifactManager?.stopWatching(); - await this.artifactManager?.waitForPendingRequests( - this.config.artifactWaitMs ?? 60_000, - ); - await this.config.uploadTracker.waitForAll( - this.config.uploadTimeoutMs ?? 30_000, - ); + await this.drain(); resolve(); }); @@ -97,6 +83,9 @@ export class WebSocketSession { switch (message.message_type) { case "end_session_message": this.logger.log("Received end_session_message. Closing session."); + // Drain the artifact watcher *before* closing so any late chokidar + // events still have an open socket to send upload requests through. + await this.drain(); this.ws.close(); return; @@ -211,6 +200,26 @@ export class WebSocketSession { } } + private drain(): Promise { + if (!this.drainPromise) { + this.drainPromise = (async () => { + const delayMs = this.config.postInvocationDelayMs ?? 3_000; + if (this.artifactManager && delayMs > 0) { + this.logger.log(`Waiting ${delayMs}ms for artifact watcher to drain...`); + await sleep(delayMs); + } + await this.artifactManager?.stopWatching(); + await this.artifactManager?.waitForPendingRequests( + this.config.artifactWaitMs ?? 60_000, + ); + await this.config.uploadTracker.waitForAll( + this.config.uploadTimeoutMs ?? 30_000, + ); + })(); + } + return this.drainPromise; + } + private ensureArtifactManager(): void { if (this.artifactManager) { this.artifactManager.setLogger(this.logger); From 79977a77703478b5f1aaadc3f9c0a01ca81b7b27 Mon Sep 17 00:00:00 2001 From: Jack Brown Date: Fri, 17 Apr 2026 16:39:46 -0700 Subject: [PATCH 2/2] bump version to 0.11.1 --- packages/runtimeuse/package-lock.json | 4 ++-- packages/runtimeuse/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtimeuse/package-lock.json b/packages/runtimeuse/package-lock.json index dce635e..a61bdd3 100644 --- a/packages/runtimeuse/package-lock.json +++ b/packages/runtimeuse/package-lock.json @@ -1,12 +1,12 @@ { "name": "runtimeuse", - "version": "0.11.0", + "version": "0.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "runtimeuse", - "version": "0.11.0", + "version": "0.11.1", "license": "FSL", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.73", diff --git a/packages/runtimeuse/package.json b/packages/runtimeuse/package.json index 5df2767..792d558 100644 --- a/packages/runtimeuse/package.json +++ b/packages/runtimeuse/package.json @@ -1,6 +1,6 @@ { "name": "runtimeuse", - "version": "0.11.0", + "version": "0.11.1", "description": "AI agent runtime with WebSocket protocol, artifact handling, and secret management", "license": "FSL", "type": "module",