mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 05:42:31 -05:00
Retain unresolved process identity and launch evidence through retryable cleanup. Share capture path normalization and preserve failed filesystem observations. Bound child output and drain Windows pipes without one-chunk throttling. Reviewed fixes include #1628, #2632, #2644, #2784, #2928, #2931, #2965, #2992, and #3011. Validation: 92 focused source tests passed; four platform-specific skips. Real Windows and Hopper lifecycle coverage was not rerun.
120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
import { z } from "zod";
|
|
import type { UnverifiedProcessCapture } from "./processCapture.js";
|
|
import type { ProcessObservationSource } from "./processObservation.js";
|
|
|
|
const byteRetentionSchema = z.strictObject({
|
|
budget_bytes: z.number().int().nonnegative(),
|
|
observed_bytes: z.number().int().nonnegative(),
|
|
retained_bytes: z.number().int().nonnegative(),
|
|
observed_frames: z.number().int().nonnegative(),
|
|
retained_frames: z.number().int().nonnegative(),
|
|
});
|
|
|
|
/** Filesystem enumeration and whole-file digest coverage for one checkpoint. */
|
|
export const filesystemCoverageSchema = z.strictObject({
|
|
files_limit: z.number().int().nonnegative(),
|
|
depth_limit: z.number().int().nonnegative(),
|
|
enumeration_truncated: z.boolean(),
|
|
enumeration_reasons: z.array(
|
|
z.enum([
|
|
"files_limit",
|
|
"depth_limit",
|
|
"identity_changed",
|
|
"directory_unavailable",
|
|
]),
|
|
),
|
|
enumeration_omissions: z.array(
|
|
z.strictObject({
|
|
path: z.string(),
|
|
reason: z.literal("directory_unavailable"),
|
|
system_code: z.string().nullable(),
|
|
}),
|
|
),
|
|
hash_budget_bytes: z.number().int().nonnegative(),
|
|
hashed_bytes: z.number().int().nonnegative(),
|
|
hash_omissions: z.array(
|
|
z.strictObject({
|
|
path: z.string(),
|
|
size_bytes: z.number().int().nonnegative(),
|
|
remaining_budget_bytes: z.number().int().nonnegative(),
|
|
reason: z.enum([
|
|
"file_bytes_budget",
|
|
"file_changed_or_short_read",
|
|
"file_unavailable",
|
|
]),
|
|
system_code: z.string().nullable(),
|
|
}),
|
|
),
|
|
});
|
|
export type FilesystemCoverage = z.infer<typeof filesystemCoverageSchema>;
|
|
|
|
/** Actual budget accounting; rendered bytes count state and visible lines, not JSON. */
|
|
export const processCaptureTruncationDetailsSchema = z.strictObject({
|
|
raw_terminal: byteRetentionSchema.describe(
|
|
"UTF-8 bytes of original PTY chunks; whole chunks are retained or omitted.",
|
|
),
|
|
rendered_terminal: byteRetentionSchema.describe(
|
|
"Cumulative UTF-8 bytes of serialized state plus visible lines, evaluated from retained PTY input; whole frames are retained or omitted.",
|
|
),
|
|
filesystem_before: filesystemCoverageSchema,
|
|
filesystem_after: filesystemCoverageSchema,
|
|
process: z.strictObject({
|
|
sample_limit: z.number().int().nonnegative(),
|
|
sampling_partial: z.boolean(),
|
|
retained_samples: z.number().int().nonnegative(),
|
|
sample_limit_reached: z.boolean(),
|
|
sampling_failures: z.number().int().nonnegative(),
|
|
first_sampling_failure: z.string().nullable(),
|
|
coverage: z.literal("sampled"),
|
|
}),
|
|
});
|
|
export type ProcessCaptureTruncationDetails = z.infer<
|
|
typeof processCaptureTruncationDetailsSchema
|
|
>;
|
|
export type TerminalRetention = ProcessCaptureTruncationDetails["raw_terminal"];
|
|
export type ProcessSamplingCoverage =
|
|
ProcessCaptureTruncationDetails["process"];
|
|
|
|
const omitted = (retention: TerminalRetention): boolean =>
|
|
retention.observed_frames > retention.retained_frames;
|
|
const filesystemTruncated = (coverage: FilesystemCoverage): boolean =>
|
|
coverage.enumeration_truncated ||
|
|
coverage.enumeration_omissions.length > 0 ||
|
|
coverage.hash_omissions.length > 0;
|
|
|
|
/** Derive the aggregate truncation flag from independent producer observations. */
|
|
export const hasCaptureTruncation = (
|
|
details: ProcessCaptureTruncationDetails,
|
|
): boolean =>
|
|
omitted(details.raw_terminal) ||
|
|
omitted(details.rendered_terminal) ||
|
|
filesystemTruncated(details.filesystem_before) ||
|
|
filesystemTruncated(details.filesystem_after) ||
|
|
details.process.sampling_partial;
|
|
|
|
/** Whether producer accounting reports a gap in the selected observation source. */
|
|
export const processSourceTruncated = (
|
|
capture: UnverifiedProcessCapture,
|
|
source: ProcessObservationSource,
|
|
): boolean => {
|
|
const details = capture.truncation_details;
|
|
switch (source) {
|
|
case "terminal_raw":
|
|
return omitted(details.raw_terminal);
|
|
case "terminal_rendered":
|
|
return (
|
|
omitted(details.raw_terminal) || omitted(details.rendered_terminal)
|
|
);
|
|
case "filesystem":
|
|
return (
|
|
filesystemTruncated(details.filesystem_before) ||
|
|
filesystemTruncated(details.filesystem_after)
|
|
);
|
|
case "process":
|
|
return details.process.sampling_partial;
|
|
case "interaction":
|
|
case "lifecycle":
|
|
return false;
|
|
}
|
|
};
|