blob: 567c3ea8bfa69c9437dbb70a1fc9d4895172846f [file]
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import { RunTrace, type RunTraceEvent } from '../run-trace.js';
describe('RunTrace error diagnostics', () => {
test('records a path-free active sandbox snapshot', () => {
const events: RunTraceEvent[] = [];
const trace = new RunTrace({
sessionId: 'session-1',
turnId: 'turn-1',
connectionSlug: 'deepseek',
providerId: 'openai-compatible',
modelId: 'deepseek-v4-pro',
newId: () => `trace-${events.length + 1}`,
now: () => 123,
record: (event) => events.push(event),
});
trace.sandboxContextResolved({
schemaVersion: 1,
platform: 'darwin',
profile: {
name: 'workspace-write',
type: 'managed',
fileSystem: 'workspace-write',
network: 'restricted',
protectedMetadata: ['.git'],
},
capabilities: {
command: {
status: 'available',
backend: 'macos-seatbelt',
selectionReason: 'platform_sandbox_selected',
},
filesystem: {
status: 'unavailable',
backend: 'macos-seatbelt',
failure: { stage: 'launch', reason: 'filesystem_worker_unavailable' },
},
},
});
assert.equal(events[0]?.type, 'sandbox_context_resolved');
assert.equal(events[0]?.phase, 'sandbox');
assert.equal(JSON.stringify(events[0]).includes('/Users/'), false);
const snapshot = events[0]?.data?.snapshot as { profile?: { name?: string } } | undefined;
assert.equal(snapshot?.profile?.name, 'workspace-write');
});
test('model stream failures keep generic copy plus redacted raw diagnostics', () => {
const events: RunTraceEvent[] = [];
const trace = new RunTrace({
sessionId: 'session-1',
turnId: 'turn-1',
connectionSlug: 'deepseek',
providerId: 'openai-compatible',
modelId: 'deepseek-v4-pro',
newId: () => `trace-${events.length + 1}`,
now: () => 123,
record: (event) => events.push(event),
});
const error = new TypeError(
'Cannot read properties of undefined (reading "role") token=sk-live-secret-token-value',
);
error.stack = [
'TypeError: Cannot read properties of undefined (reading "role") token=sk-live-secret-token-value',
' at projectRequest (file:///repo/packages/runtime/src/ai-sdk-backend.ts:123:45)',
].join('\n');
trace.modelStreamFailed('TypeError', error);
assert.equal(events.length, 1);
const data = events[0]?.data ?? {};
assert.equal(data.errorClass, 'TypeError');
assert.equal(data.error, 'Operation failed');
assert.equal(data.rawErrorName, 'TypeError');
assert.equal(data.rawErrorType, 'object');
assert.match(String(data.redactedErrorMessage), /Cannot read properties/);
assert.match(String(data.redactedErrorMessage), /token=\[redacted\]/);
assert.match(String(data.redactedErrorMessageSha256), /^sha256:[a-f0-9]{64}$/);
assert.match(String(data.redactedErrorStackSha256), /^sha256:[a-f0-9]{64}$/);
assert.equal(JSON.stringify(data).includes('sk-live-secret-token-value'), false);
});
test('model stream failure diagnostics are bounded', () => {
const events: RunTraceEvent[] = [];
const trace = new RunTrace({
sessionId: 'session-1',
turnId: 'turn-1',
connectionSlug: 'deepseek',
providerId: 'openai-compatible',
modelId: 'deepseek-v4-pro',
newId: () => `trace-${events.length + 1}`,
now: () => 123,
record: (event) => events.push(event),
});
trace.modelStreamFailed(undefined, new Error('x'.repeat(3_000)));
const data = events[0]?.data ?? {};
assert.equal(String(data.redactedErrorMessage).length, 2_048);
assert.equal(data.redactedErrorMessageTruncated, true);
assert.match(String(data.redactedErrorMessageSha256), /^sha256:[a-f0-9]{64}$/);
});
test('model stream failure diagnostics preserve redacted structured error fields', () => {
const events: RunTraceEvent[] = [];
const trace = new RunTrace({
sessionId: 'session-1',
turnId: 'turn-1',
connectionSlug: 'sub2api',
providerId: 'openai',
modelId: 'gpt-test',
newId: () => `trace-${events.length + 1}`,
now: () => 123,
record: (event) => events.push(event),
});
trace.modelStreamFailed('Other', {
status: 502,
code: 'upstream_reset',
message: 'upstream stream reset',
apiKey: 'sk-live-secret-token-value',
});
const data = events[0]?.data ?? {};
assert.equal(data.rawErrorName, 'object');
assert.equal(data.rawErrorType, 'object');
assert.match(String(data.redactedErrorMessage), /upstream stream reset/);
assert.match(String(data.redactedErrorMessage), /upstream_reset/);
assert.match(String(data.redactedErrorMessage), /"apiKey":"\[redacted\]"/);
assert.equal(String(data.redactedErrorMessage).includes('sk-live-secret-token-value'), false);
});
});