@tracecode/harness 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project are documented here.
4
4
 
5
5
  This repo uses Git tags as release boundaries. Version notes below summarize what shipped in each tagged release.
6
6
 
7
+ ## [0.9.9] - 2026-07-05
8
+
9
+ ### Added
10
+
11
+ - Added a unified kernel journal: one append-only, absolutely-ordered log (a single sequence counter) of every kernel-observed transaction — filesystem writes, process `exec`/`exit`, and HTTP requests — emitted only from kernel-internal observation points, so in-workspace code cannot forge entries and every event is attributed by actor/pid. `Authorization` is stored as a non-reversible fingerprint (never the raw value), and `externalHttp` responses may attach an opaque `annotation`. The journal is exposed both live on the workspace event stream (`kernel-journal` events, ordered consistently with buffered output) and as a queryable `journal(sinceSeq?)` snapshot. HTTP journal records additionally carry redacted grading metadata: idempotency-key and request/response body fingerprints, plus `Content-Type`, `Retry-After`, and `X-RateLimit-*` values.
12
+ - Added a virtual-network host registry (`resolveHost`) and a `ping` reachability command: loopback, in-workspace HTTP listeners, and `externalHttp`-allowlisted hosts all resolve through one primitive with deterministic, hash-derived synthetic IP and latency (no wall clock or RNG). `ping` produces ping-shaped output and fails gracefully with an unknown-host error instead of a raw kernel throw.
13
+
14
+ ### Changed
15
+
16
+ - Unified host reachability across `curl`, `ping`, and `workspace.http.request` through `resolveHost`: an unknown host now returns a typed `EHOSTUNREACH` (rendered by `curl` as exit 7, "Host unreachable") rather than leaking a raw kernel error, while a known host with a closed port still returns `ECONNREFUSED`; host allowlist/blocklist policy is unchanged. Also corrected the diagnostic port reported for failed HTTPS connections.
17
+ - Optimized C++ and C# batch execution: test cases that are safe to co-execute now share a single compile-and-run pass, with an automatic per-case fallback when a batch requires isolation.
18
+
19
+ ### Fixed
20
+
21
+ - Fixed `curl` URL scheme resolution and replaced raw kernel HTTP errors with typed ones so nothing leaks to the terminal: bare hostnames, `host:port`, and `localhost:3000` now resolve correctly, unsupported schemes return a proper `curl` protocol error, and malformed requests surface as graceful `curl` diagnostics instead of a raw `EINVAL`.
22
+
7
23
  ## [0.9.8] - 2026-07-04
8
24
 
9
25
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tracecode/harness",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "license": "AGPL-3.0-only",
5
5
  "homepage": "https://tracecode.app",
6
6
  "repository": {
@@ -10518,6 +10518,80 @@ async function handleCompileRun(payload) {
10518
10518
  }
10519
10519
  }
10520
10520
 
10521
+ function cppBatchIsolationReason(source) {
10522
+ const text = stripComments(String(source ?? ''));
10523
+ if (/\b(?:static|thread_local)\b/.test(text)) {
10524
+ return 'static-storage';
10525
+ }
10526
+ if (cppHasFileScopeMutableDeclaration(text)) {
10527
+ return 'file-scope-state';
10528
+ }
10529
+ return '';
10530
+ }
10531
+
10532
+ function cppHasFileScopeMutableDeclaration(source) {
10533
+ let depth = 0;
10534
+ let statement = '';
10535
+
10536
+ const considerStatement = (rawStatement) => {
10537
+ const normalized = rawStatement
10538
+ .replace(/#[^\n]*(?:\n|$)/g, '')
10539
+ .replace(/\s+/g, ' ')
10540
+ .trim();
10541
+ if (!normalized) return false;
10542
+ if (/^(?:using|typedef|template|class|struct|enum|namespace|extern\s+"C")\b/.test(normalized)) return false;
10543
+ if (/\b(?:const|constexpr)\b/.test(normalized)) return false;
10544
+ if (/\(/.test(normalized)) return false;
10545
+ if (/^(?:return|if|for|while|switch|do|break|continue)\b/.test(normalized)) return false;
10546
+ return /^(?:inline\s+|volatile\s+|mutable\s+|unsigned\s+|signed\s+|long\s+|short\s+)*[A-Za-z_:][A-Za-z0-9_:<>,\s*&]*\s+[*&\s]*[A-Za-z_][A-Za-z0-9_]*(?:\s*(?:=|,|\[|;))/.test(normalized);
10547
+ };
10548
+
10549
+ for (const char of source) {
10550
+ if (depth === 0) {
10551
+ statement += char;
10552
+ }
10553
+ if (char === '{') {
10554
+ depth += 1;
10555
+ continue;
10556
+ }
10557
+ if (char === '}') {
10558
+ depth = Math.max(0, depth - 1);
10559
+ continue;
10560
+ }
10561
+ if (depth === 0 && char === ';') {
10562
+ if (considerStatement(statement)) return true;
10563
+ statement = '';
10564
+ }
10565
+ }
10566
+
10567
+ return false;
10568
+ }
10569
+
10570
+ function cppOpsClassBatchIsolationReason(inputBatch) {
10571
+ if (!Array.isArray(inputBatch) || inputBatch.length <= 1) return '';
10572
+ const firstOperations = Array.isArray(inputBatch[0]?.operations)
10573
+ ? inputBatch[0].operations
10574
+ : Array.isArray(inputBatch[0]?.ops)
10575
+ ? inputBatch[0].ops
10576
+ : [];
10577
+ for (let caseIndex = 1; caseIndex < inputBatch.length; caseIndex += 1) {
10578
+ const operations = Array.isArray(inputBatch[caseIndex]?.operations)
10579
+ ? inputBatch[caseIndex].operations
10580
+ : Array.isArray(inputBatch[caseIndex]?.ops)
10581
+ ? inputBatch[caseIndex].ops
10582
+ : [];
10583
+ if (operations.length !== firstOperations.length) {
10584
+ return 'heterogeneous-ops-class';
10585
+ }
10586
+ for (let operationIndex = 0; operationIndex < operations.length; operationIndex += 1) {
10587
+ if (operations[operationIndex] !== firstOperations[operationIndex]) {
10588
+ return 'heterogeneous-ops-class';
10589
+ }
10590
+ }
10591
+ }
10592
+ return '';
10593
+ }
10594
+
10521
10595
  async function handleCompileRunBatch(payload) {
10522
10596
  const startedAt = now();
10523
10597
  const source = payload && typeof payload.code === 'string' ? payload.code : '';
@@ -10562,17 +10636,94 @@ async function handleCompileRunBatch(payload) {
10562
10636
  };
10563
10637
  }
10564
10638
 
10565
- const results = [];
10566
- for (const inputs of inputBatch) {
10567
- results.push(await handleCompileRun({ ...payload, inputs, executionStyle }));
10639
+ const isolationReason = executionStyle === 'ops-class'
10640
+ ? (cppOpsClassBatchIsolationReason(inputBatch) || cppBatchIsolationReason(source))
10641
+ : cppBatchIsolationReason(source);
10642
+ if (isolationReason) {
10643
+ const results = [];
10644
+ for (const inputs of inputBatch) {
10645
+ results.push(await handleCompileRun({ ...payload, inputs, executionStyle }));
10646
+ }
10647
+ const success = results.every((result) => result.success === true);
10648
+ return {
10649
+ success,
10650
+ results,
10651
+ consoleOutput: results.flatMap((result) => result.consoleOutput ?? []),
10652
+ ...(success ? {} : { error: results.find((result) => result.success !== true)?.error ?? 'C++ batch execution failed.' }),
10653
+ timings: {
10654
+ ...baseTimings(),
10655
+ batchMode: 'per-case-fallback',
10656
+ batchCaseCount: inputBatch.length,
10657
+ batchFallbackReason: isolationReason,
10658
+ },
10659
+ };
10660
+ }
10661
+
10662
+ let preparedDriverSource;
10663
+ try {
10664
+ preparedDriverSource = executionStyle === 'ops-class'
10665
+ ? buildOpsClassBatchDriverSource(source, functionName, inputBatch, { executionStyle })
10666
+ : buildBatchDriverSource(source, functionName, inputBatch, { executionStyle });
10667
+ } catch (error) {
10668
+ return failedBatchResult(error instanceof Error ? error.message : String(error));
10669
+ }
10670
+
10671
+ const batchTimings = (timings = {}) => ({
10672
+ ...timings,
10673
+ totalMs: elapsedMs(startedAt),
10674
+ batchMode: 'compile-once',
10675
+ batchCaseCount: inputBatch.length,
10676
+ });
10677
+
10678
+ const failedBatchEntries = (result) => inputBatch.map(() => ({
10679
+ success: false,
10680
+ output: null,
10681
+ error: result?.error ?? 'C++ batch execution failed.',
10682
+ consoleOutput: result?.consoleOutput ?? [],
10683
+ timings: result?.timings ?? {},
10684
+ }));
10685
+
10686
+ const result = await compileAndRun(source, functionName, {}, {
10687
+ executionStyle,
10688
+ preparedDriverSource,
10689
+ stdinText: JSON.stringify(inputBatch),
10690
+ });
10691
+
10692
+ if (!result?.success) {
10693
+ return {
10694
+ success: false,
10695
+ results: failedBatchEntries(result),
10696
+ consoleOutput: result?.consoleOutput ?? [],
10697
+ error: result?.error ?? 'C++ batch execution failed.',
10698
+ timings: batchTimings(result?.timings),
10699
+ };
10568
10700
  }
10569
- const success = results.every((result) => result.success === true);
10701
+
10702
+ if (!Array.isArray(result.output) || result.output.length !== inputBatch.length) {
10703
+ const error = `C++ batch driver returned ${Array.isArray(result.output) ? result.output.length : 'non-array'} results for ${inputBatch.length} cases.`;
10704
+ return {
10705
+ success: false,
10706
+ results: failedBatchEntries({ ...result, error }),
10707
+ consoleOutput: result.consoleOutput ?? [],
10708
+ error,
10709
+ timings: batchTimings(result.timings),
10710
+ };
10711
+ }
10712
+
10713
+ const results = result.output.map((output, index) => ({
10714
+ success: true,
10715
+ output,
10716
+ consoleOutput: index === 0 ? (result.consoleOutput ?? []) : [],
10717
+ executionTimeMs: index === 0 ? result.executionTimeMs : 0,
10718
+ timings: index === 0
10719
+ ? { ...(result.timings ?? {}), batchCaseIndex: index }
10720
+ : { runMs: 0, totalMs: 0, compileCacheHit: true, batchCaseIndex: index },
10721
+ }));
10570
10722
  return {
10571
- success,
10723
+ success: true,
10572
10724
  results,
10573
- consoleOutput: results.flatMap((result) => result.consoleOutput ?? []),
10574
- ...(success ? {} : { error: results.find((result) => result.success !== true)?.error ?? 'C++ batch execution failed.' }),
10575
- timings: { ...baseTimings(), batchMode: 'per-case-isolated', batchCaseCount: inputBatch.length },
10725
+ consoleOutput: result.consoleOutput ?? [],
10726
+ timings: batchTimings(result.timings),
10576
10727
  };
10577
10728
  }
10578
10729
 
@@ -1807,11 +1807,173 @@ function indentCSharpSource(code, spaces = 4) {
1807
1807
  .join('\n');
1808
1808
  }
1809
1809
 
1810
+ function splitCSharpParameterList(source) {
1811
+ const parameters = [];
1812
+ let current = '';
1813
+ let genericDepth = 0;
1814
+ let bracketDepth = 0;
1815
+ for (const char of String(source ?? '')) {
1816
+ if (char === '<') genericDepth += 1;
1817
+ else if (char === '>') genericDepth = Math.max(0, genericDepth - 1);
1818
+ else if (char === '[') bracketDepth += 1;
1819
+ else if (char === ']') bracketDepth = Math.max(0, bracketDepth - 1);
1820
+ if (char === ',' && genericDepth === 0 && bracketDepth === 0) {
1821
+ parameters.push(current.trim());
1822
+ current = '';
1823
+ continue;
1824
+ }
1825
+ current += char;
1826
+ }
1827
+ if (current.trim()) parameters.push(current.trim());
1828
+ return parameters;
1829
+ }
1830
+
1831
+ function parseCSharpBatchParameter(source, index) {
1832
+ let withoutDefault = String(source ?? '').replace(/=.*/, '').trim();
1833
+ while (/^\s*\[[^\]]+\]\s*/.test(withoutDefault)) {
1834
+ withoutDefault = withoutDefault.replace(/^\s*\[[^\]]+\]\s*/, '').trim();
1835
+ }
1836
+ if (!withoutDefault) return null;
1837
+ const parts = withoutDefault.split(/\s+/).filter(Boolean);
1838
+ let modifier = '';
1839
+ while (parts.length > 0 && ['this', 'params', 'ref', 'out', 'in'].includes(parts[0])) {
1840
+ const next = parts.shift();
1841
+ if (next === 'ref' || next === 'out' || next === 'in') modifier = next;
1842
+ }
1843
+ const name = csharpIdentifier(parts.pop());
1844
+ const type = parts.join(' ').trim();
1845
+ if (!name || !type) return null;
1846
+ return { name, type, modifier, index };
1847
+ }
1848
+
1849
+ function parseCSharpBatchCallableSignature(code, functionName) {
1850
+ const requestedMethodName = csharpIdentifier(functionName);
1851
+ if (!requestedMethodName) return null;
1852
+ const escapedName = requestedMethodName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1853
+ const pattern = new RegExp(
1854
+ `((?:(?:public|private|protected|internal|static|virtual|override|sealed|new|async|extern|partial)\\s+)*)` +
1855
+ `([A-Za-z_][A-Za-z0-9_<>.,?\\[\\]\\s]*?)\\s+(${escapedName})\\s*\\(([^)]*)\\)`,
1856
+ 'im'
1857
+ );
1858
+ const match = pattern.exec(String(code ?? ''));
1859
+ if (!match) return null;
1860
+ const methodName = csharpIdentifier(match[3]) ?? requestedMethodName;
1861
+ const parameters = splitCSharpParameterList(match[4])
1862
+ .map((parameter, index) => parseCSharpBatchParameter(parameter, index));
1863
+ if (parameters.some((parameter) => !parameter)) return null;
1864
+ return {
1865
+ methodName,
1866
+ returnType: match[2].trim(),
1867
+ isStatic: /\bstatic\b/.test(match[1] || ''),
1868
+ parameters,
1869
+ };
1870
+ }
1871
+
1872
+ function buildCSharpDirectBatchScriptSource(payload) {
1873
+ const code = String(payload?.code ?? '');
1874
+ const executionStyle = payload?.executionStyle ?? 'solution-method';
1875
+ const functionName = String(payload?.functionName ?? '');
1876
+ if (!functionName.trim() || executionStyle === 'ops-class') return null;
1877
+ const signature = parseCSharpBatchCallableSignature(code, functionName);
1878
+ if (!signature) return null;
1879
+ const declarations = [];
1880
+ const argumentsList = [];
1881
+ for (const parameter of signature.parameters) {
1882
+ const localName = `__tracecodeArg${parameter.index}`;
1883
+ if (parameter.modifier === 'out') {
1884
+ declarations.push(` ${parameter.type} ${localName} = default!;`);
1885
+ } else {
1886
+ declarations.push(` var ${localName} = TraceCode.Internal.TraceCodeJsonInput.Read<${parameter.type}>(${csharpStringLiteral(parameter.name)}, ${parameter.index});`);
1887
+ }
1888
+ argumentsList.push(`${parameter.modifier ? `${parameter.modifier} ` : ''}${localName}`);
1889
+ }
1890
+ const receiver = executionStyle === 'function'
1891
+ ? ''
1892
+ : signature.isStatic
1893
+ ? 'Solution.'
1894
+ : '__tracecodeSolution.';
1895
+ const solutionDeclaration = executionStyle === 'function' || signature.isStatic
1896
+ ? ''
1897
+ : ' var __tracecodeSolution = new Solution();\n';
1898
+ const callExpression = `${receiver}${signature.methodName}(${argumentsList.join(', ')})`;
1899
+ const returnsVoid = signature.returnType === 'void';
1900
+ const invocation = returnsVoid
1901
+ ? ` ${callExpression};\n return ${signature.parameters.length > 0 ? '__tracecodeArg0' : 'null'};`
1902
+ : ` return ${callExpression};`;
1903
+
1904
+ return `${code}
1905
+
1906
+ object? result;
1907
+ {
1908
+ var __tracecodeBatchCases = TraceCode.Internal.TraceCodeJsonInput.Read<System.Text.Json.JsonElement[]>("__tracecodeBatchInputs", 0) ?? System.Array.Empty<System.Text.Json.JsonElement>();
1909
+ var __tracecodeBatchResults = new System.Collections.Generic.List<object?>();
1910
+
1911
+ foreach (var __tracecodeBatchCase in __tracecodeBatchCases)
1912
+ {
1913
+ var __tracecodeBatchClock = System.Diagnostics.Stopwatch.StartNew();
1914
+ var __tracecodeOriginalOut = System.Console.Out;
1915
+ using var __tracecodeCaseOut = new System.IO.StringWriter();
1916
+ try
1917
+ {
1918
+ System.Console.SetOut(__tracecodeCaseOut);
1919
+ __TraceCodeSetCurrentInputsJson(__tracecodeBatchCase.GetRawText());
1920
+ object? __tracecodeOutput = __TraceCodeRunBatchCase();
1921
+ __tracecodeBatchClock.Stop();
1922
+ __tracecodeBatchResults.Add(new System.Collections.Generic.Dictionary<string, object?>
1923
+ {
1924
+ ["success"] = true,
1925
+ ["output"] = __tracecodeOutput,
1926
+ ["consoleOutput"] = __TraceCodeSplitConsole(__tracecodeCaseOut.ToString()),
1927
+ ["timings"] = new System.Collections.Generic.Dictionary<string, object?> { ["runMs"] = __tracecodeBatchClock.Elapsed.TotalMilliseconds },
1928
+ });
1929
+ }
1930
+ catch (System.Exception __tracecodeError)
1931
+ {
1932
+ __tracecodeBatchClock.Stop();
1933
+ __tracecodeBatchResults.Add(new System.Collections.Generic.Dictionary<string, object?>
1934
+ {
1935
+ ["success"] = false,
1936
+ ["error"] = __tracecodeError.GetBaseException().Message,
1937
+ ["output"] = null,
1938
+ ["consoleOutput"] = __TraceCodeSplitConsole(__tracecodeCaseOut.ToString()),
1939
+ ["timings"] = new System.Collections.Generic.Dictionary<string, object?> { ["runMs"] = __tracecodeBatchClock.Elapsed.TotalMilliseconds },
1940
+ });
1941
+ }
1942
+ finally
1943
+ {
1944
+ System.Console.SetOut(__tracecodeOriginalOut);
1945
+ }
1946
+ }
1947
+
1948
+ result = __tracecodeBatchResults;
1949
+ }
1950
+
1951
+ object? __TraceCodeRunBatchCase()
1952
+ {
1953
+ ${solutionDeclaration}${declarations.join('\n')}
1954
+ ${invocation}
1955
+ }
1956
+
1957
+ void __TraceCodeSetCurrentInputsJson(string __tracecodeInputsJson)
1958
+ {
1959
+ var __tracecodeField = typeof(TraceCode.CSharpHost.CompilerHost).GetField(
1960
+ "currentInputsJson",
1961
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
1962
+ __tracecodeField?.SetValue(null, __tracecodeInputsJson);
1963
+ }
1964
+
1965
+ string[] __TraceCodeSplitConsole(string __tracecodeText) =>
1966
+ __tracecodeText.Replace("\\r\\n", "\\n", System.StringComparison.Ordinal).Split('\\n', System.StringSplitOptions.RemoveEmptyEntries);
1967
+ `;
1968
+ }
1969
+
1810
1970
  function buildCSharpBatchScriptSource(payload) {
1811
1971
  const code = String(payload?.code ?? '');
1812
1972
  const executionStyle = payload?.executionStyle ?? 'solution-method';
1813
1973
  const functionName = String(payload?.functionName ?? '');
1814
1974
  const functionNameLiteral = csharpStringLiteral(functionName);
1975
+ const directBatchSource = buildCSharpDirectBatchScriptSource(payload);
1976
+ if (directBatchSource) return directBatchSource;
1815
1977
 
1816
1978
  if (executionStyle === 'ops-class') {
1817
1979
  const className = csharpIdentifier(functionName);
@@ -2243,6 +2405,17 @@ function normalizeCSharpBatchEntry(entry, timings = {}) {
2243
2405
  };
2244
2406
  }
2245
2407
 
2408
+ function csharpBatchIsolationReason(payload) {
2409
+ if (payload?.executionStyle === 'ops-class') {
2410
+ return 'ops-class-reflection';
2411
+ }
2412
+ const code = String(payload?.code ?? '');
2413
+ if (/\bstatic\b/.test(code)) {
2414
+ return 'static-storage';
2415
+ }
2416
+ return '';
2417
+ }
2418
+
2246
2419
  async function executeCSharpCodePayload(payload, messageType = 'execute-code') {
2247
2420
  const startedAt = now();
2248
2421
  const request = {
@@ -2315,21 +2488,63 @@ async function executeCSharpCodeBatch(message) {
2315
2488
  };
2316
2489
  }
2317
2490
 
2318
- const results = [];
2319
- for (const inputs of inputBatch) {
2320
- const result = await executeCSharpCodePayload({ ...message.payload, inputs }, 'execute-code');
2321
- results.push(normalizeCSharpBatchEntry(result, result.timings));
2491
+ const isolationReason = csharpBatchIsolationReason(message.payload ?? {});
2492
+ if (isolationReason) {
2493
+ const results = [];
2494
+ for (const inputs of inputBatch) {
2495
+ const result = await executeCSharpCodePayload({ ...message.payload, inputs }, 'execute-code');
2496
+ results.push(normalizeCSharpBatchEntry(result, result.timings));
2497
+ }
2498
+ const consoleOutput = results.flatMap((entry) => entry.consoleOutput ?? []);
2499
+ const success = results.every((entry) => entry.success === true);
2500
+ return {
2501
+ success,
2502
+ results,
2503
+ consoleOutput,
2504
+ ...(success ? {} : { error: results.find((entry) => entry.success !== true)?.error ?? 'C# batch execution failed.' }),
2505
+ timings: {
2506
+ totalMs: elapsedMs(startedAt),
2507
+ batchMode: 'per-case-fallback',
2508
+ batchCaseCount: inputBatch.length,
2509
+ batchFallbackReason: isolationReason,
2510
+ runMs: results.reduce((sum, entry) => sum + (entry.timings?.runMs ?? 0), 0),
2511
+ },
2512
+ };
2322
2513
  }
2514
+
2515
+ let batchSource;
2516
+ try {
2517
+ batchSource = buildCSharpBatchScriptSource(message.payload ?? {});
2518
+ } catch (error) {
2519
+ return {
2520
+ success: false,
2521
+ results: [],
2522
+ error: error instanceof Error ? error.message : String(error),
2523
+ consoleOutput: [],
2524
+ timings: { totalMs: elapsedMs(startedAt) },
2525
+ };
2526
+ }
2527
+
2528
+ const result = await executeCSharpCodePayload({
2529
+ ...message.payload,
2530
+ code: batchSource,
2531
+ functionName: '',
2532
+ executionStyle: 'function',
2533
+ inputs: { __tracecodeBatchInputs: inputBatch },
2534
+ }, 'execute-code');
2535
+ const batchEntries = Array.isArray(result?.output) ? result.output : [];
2536
+ const results = batchEntries.map((entry) => normalizeCSharpBatchEntry(entry));
2323
2537
  const consoleOutput = results.flatMap((entry) => entry.consoleOutput ?? []);
2324
2538
  const success = results.length === inputBatch.length && results.every((entry) => entry.success === true);
2325
2539
  return {
2326
2540
  success,
2327
2541
  results,
2328
2542
  consoleOutput,
2329
- ...(success ? {} : { error: results.find((entry) => entry.success !== true)?.error ?? 'C# batch execution failed.' }),
2543
+ ...(success ? {} : { error: result?.error ?? results.find((entry) => entry.success !== true)?.error ?? 'C# batch execution failed.' }),
2330
2544
  timings: {
2545
+ ...(result?.timings && typeof result.timings === 'object' ? result.timings : {}),
2331
2546
  totalMs: elapsedMs(startedAt),
2332
- batchMode: 'per-case-isolated',
2547
+ batchMode: 'compile-once',
2333
2548
  batchCaseCount: inputBatch.length,
2334
2549
  runMs: results.reduce((sum, entry) => sum + (entry.timings?.runMs ?? 0), 0),
2335
2550
  },
@@ -3716,7 +3716,7 @@ function unzipSync(data, opts) {
3716
3716
  // packages/harness-javascript/package.json
3717
3717
  var package_default = {
3718
3718
  name: "@tracecode/harness-javascript",
3719
- version: "0.9.8",
3719
+ version: "0.9.9",
3720
3720
  description: "JavaScript and TypeScript runtime helpers and browser worker assets for TraceCode harness.",
3721
3721
  license: "AGPL-3.0-only",
3722
3722
  homepage: "https://tracecode.app",