From 45ae4f2457e68741717dbc198fb4958d18d67c3f Mon Sep 17 00:00:00 2001 From: lemonzoey <25550414+lemonzoey@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:11:23 +0800 Subject: [PATCH] fix(code-interpreter): use backend timestamp in JS OutputMessage parseOutput set OutputMessage.timestamp to new Date().getTime() * 1000 (client clock, microseconds), ignoring the timestamp the kernel sends with each stdout/stderr message. Read msg.timestamp instead, matching the documented 'Unix epoch in nanoseconds' contract and the Python SDK (which passes data["timestamp"] straight through). Type unchanged (number). Closes #1969. --- .changeset/js-output-backend-timestamp.md | 5 ++ packages/code-interpreter-js/src/messaging.ts | 4 +- .../tests/messaging.test.ts | 50 ++++++++++++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 .changeset/js-output-backend-timestamp.md diff --git a/.changeset/js-output-backend-timestamp.md b/.changeset/js-output-backend-timestamp.md new file mode 100644 index 0000000000..fb6b6180cd --- /dev/null +++ b/.changeset/js-output-backend-timestamp.md @@ -0,0 +1,5 @@ +--- +'@e2b/code-interpreter': patch +--- + +Fix `OutputMessage.timestamp` in the JS Code Interpreter to use the backend timestamp (Unix epoch in nanoseconds) sent with each stdout/stderr message, instead of the client clock (`new Date().getTime() * 1000`). The type is unchanged (`number`); this aligns the value with the documented contract and the Python SDK. diff --git a/packages/code-interpreter-js/src/messaging.ts b/packages/code-interpreter-js/src/messaging.ts index c145f5b46f..4a887edc01 100644 --- a/packages/code-interpreter-js/src/messaging.ts +++ b/packages/code-interpreter-js/src/messaging.ts @@ -352,7 +352,7 @@ export async function parseOutput( await onStdout({ error: false, line: msg.text, - timestamp: new Date().getTime() * 1000, + timestamp: msg.timestamp, }) } break @@ -362,7 +362,7 @@ export async function parseOutput( await onStderr({ error: true, line: msg.text, - timestamp: new Date().getTime() * 1000, + timestamp: msg.timestamp, }) } break diff --git a/packages/code-interpreter-js/tests/messaging.test.ts b/packages/code-interpreter-js/tests/messaging.test.ts index c0add5149c..8256696b22 100644 --- a/packages/code-interpreter-js/tests/messaging.test.ts +++ b/packages/code-interpreter-js/tests/messaging.test.ts @@ -1,7 +1,12 @@ import { expect, test } from 'vitest' import { SandboxError } from '../src' -import { extractError } from '../src/messaging' +import { + Execution, + OutputMessage, + extractError, + parseOutput, +} from '../src/messaging' test('includes the response body in server errors', async () => { const error = await extractError( @@ -25,3 +30,46 @@ test('falls back to the status text for an empty body', async () => { expect(error).toEqual(new SandboxError('500 Internal Server Error')) }) + +test('stdout output carries the backend timestamp, not the client clock', async () => { + const backendTimestamp = 1700000000000000000 + const execution = new Execution() + let received: OutputMessage | undefined + + await parseOutput( + execution, + JSON.stringify({ + type: 'stdout', + text: 'hello', + timestamp: backendTimestamp, + }), + (msg) => { + received = msg + } + ) + + expect(received?.timestamp).toBe(backendTimestamp) + expect(received?.error).toBe(false) +}) + +test('stderr output carries the backend timestamp, not the client clock', async () => { + const backendTimestamp = 1700000000000000000 + const execution = new Execution() + let received: OutputMessage | undefined + + await parseOutput( + execution, + JSON.stringify({ + type: 'stderr', + text: 'boom', + timestamp: backendTimestamp, + }), + undefined, + (msg) => { + received = msg + } + ) + + expect(received?.timestamp).toBe(backendTimestamp) + expect(received?.error).toBe(true) +})