Merge pull request #421 from morluto/codex/fix-process-resize-timestamps

fix(process): normalize resize frame timestamps
This commit is contained in:
morluto 2026-07-23 07:22:35 +08:00 committed by GitHub
commit 74d1c7b274
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 10 deletions

View File

@ -6,7 +6,10 @@ import type {
ProcessScenario,
RecordProcessCaptureEvent,
} from "../domain/processCapture.js";
import { normalizeProcessText } from "./ProcessNormalization.js";
import {
normalizeProcessElapsedTime,
normalizeProcessText,
} from "./ProcessNormalization.js";
import type { TerminalRenderer } from "./TerminalRenderer.js";
/** Mutable observation-order ledger whose writer is shared by capture producers. */
@ -55,7 +58,14 @@ export const scheduleScenarioInteractions = (
if (event.type === "input") terminal.write(event.data);
else if (event.type === "resize") {
terminal.resize(event.columns, event.rows);
renderer.resize(event.columns, event.rows, dispatchedAt);
renderer.resize(
event.columns,
event.rows,
normalizeProcessElapsedTime(
dispatchedAt,
scenario.normalization.time_bucket_ms,
),
);
} else terminal.kill(event.signal);
} catch {
outcome = "failed";

View File

@ -41,6 +41,7 @@ import {
assertRealPathAuthority,
} from "./ProcessCaptureAuthority.js";
import {
normalizeProcessElapsedTime,
normalizeProcessSamples,
normalizeProcessText,
redactProtocolEvents,
@ -364,11 +365,10 @@ export const captureTerminalFrames = (options: {
return;
}
outputBytes += bytes;
const atMs =
Math.floor(
(Date.now() - options.started) /
options.scenario.normalization.time_bucket_ms,
) * options.scenario.normalization.time_bucket_ms;
const atMs = normalizeProcessElapsedTime(
Date.now() - options.started,
options.scenario.normalization.time_bucket_ms,
);
const normalized = normalizeProcessText(
data,
options.scenario,

View File

@ -4,6 +4,12 @@ import type {
ProcessScenario,
} from "../domain/processCapture.js";
/** Bucket one elapsed process-capture timestamp under scenario normalization. */
export const normalizeProcessElapsedTime = (
elapsedMs: number,
timeBucketMs: number,
): number => Math.floor(elapsedMs / timeBucketMs) * timeBucketMs;
/** Normalize and redact one terminal/protocol payload under scenario rules. */
export const normalizeProcessText = (
value: string,
@ -69,9 +75,10 @@ export const normalizeProcessSamples = (
: normalized;
};
return samples.map((sample) => ({
at_ms:
Math.floor(sample.at_ms / scenario.normalization.time_bucket_ms) *
at_ms: normalizeProcessElapsedTime(
sample.at_ms,
scenario.normalization.time_bucket_ms,
),
pid: mapping.get(sample.pid) ?? 1,
parent_pid: mapping.get(sample.parent_pid) ?? 0,
process_group_id:

View File

@ -1694,6 +1694,7 @@ describe("process capture adapter", () => {
{ type: "resize", at_ms: 300, columns: 100, rows: 40 },
{ type: "signal", at_ms: 700, signal: "SIGINT" },
],
normalization: { time_bucket_ms: 60_000 },
timeout_ms: 2_000,
idle_timeout_ms: 2_000,
}),
@ -1705,8 +1706,8 @@ describe("process capture adapter", () => {
allowExternalNetwork: true,
},
);
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
expect(result.ok).toBe(true);
const output = result.value.frames.map(({ data }) => data).join("");
expect(output).toContain("prompt>");
expect(output).toContain("input:answer unicode:雪");