mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 03:45:39 +00:00
* feat(browser): capture correlated network content and lifecycle evidence Keep request identities and redirect relationships across ordered scenario events. Capture selected browser-exposed bytes and duplicate headers through modular readers, settle bounded reads before cleanup, and preserve unavailable states. Keep content-selection mismatches and legacy network coverage unknown during comparison. Verify real CLI/MCP, installed package, and browser lifecycle paths. Refs #774 Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) * fix(browser): align capture defaults and redact encoded media metadata Correct the complete documented request and legacy default expectation caught by CI. Cover and redact encoded declared values in captured body media types. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) * test(browser): verify polling capture cutoff completeness Accept incomplete event coverage only when the continuously polling fixture has explicit unfinished requests; retain failures for other missing or truncated sections. Include actual completeness and unfinished transactions in diagnostics. Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis) Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { z } from "zod";
|
|
|
|
/** Caller-selected network content; omission retains metadata alone. */
|
|
export const browserNetworkContentSelectionValuesSchema = z.strictObject({
|
|
request_body: z.boolean().default(false),
|
|
response_body: z.boolean().default(false),
|
|
header_values: z.boolean().default(false),
|
|
});
|
|
|
|
/** Input omission selects metadata without defaulting legacy capture coverage. */
|
|
export const browserNetworkContentSelectionSchema =
|
|
browserNetworkContentSelectionValuesSchema.default({
|
|
request_body: false,
|
|
response_body: false,
|
|
header_values: false,
|
|
});
|
|
|
|
/** Self-verifying retained bytes, after any declared-secret redaction. */
|
|
export const browserNetworkBodySchema = z.union([
|
|
z.strictObject({ state: z.literal("not_requested") }),
|
|
z.strictObject({ state: z.literal("not_exposed") }),
|
|
z.strictObject({
|
|
state: z.literal("unavailable"),
|
|
reason: z.enum([
|
|
"request-failed",
|
|
"response-unfinished",
|
|
"body-read-failed",
|
|
"body-read-timeout",
|
|
"cancelled",
|
|
"capture-ended",
|
|
]),
|
|
message: z.string().min(1),
|
|
}),
|
|
z
|
|
.strictObject({
|
|
state: z.literal("captured"),
|
|
representation: z.enum([
|
|
"browser-exposed-request-bytes",
|
|
"browser-decoded-response-bytes",
|
|
]),
|
|
encoding: z.literal("base64"),
|
|
content: z.string(),
|
|
bytes: z.number().int().min(0),
|
|
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
media_type: z.string().nullable(),
|
|
redacted: z.boolean(),
|
|
})
|
|
.superRefine((artifact, context) => {
|
|
const bytes = Buffer.from(artifact.content, "base64");
|
|
if (bytes.toString("base64") !== artifact.content)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["content"],
|
|
message: "Network body must use canonical base64",
|
|
});
|
|
if (bytes.length !== artifact.bytes)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["bytes"],
|
|
message: "Network body byte count disagrees with retained bytes",
|
|
});
|
|
if (createHash("sha256").update(bytes).digest("hex") !== artifact.sha256)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["sha256"],
|
|
message: "Network body digest disagrees with retained bytes",
|
|
});
|
|
}),
|
|
]);
|
|
|
|
/** Headers retain producer order and duplicates, with credential values removed. */
|
|
export const browserNetworkHeadersSchema = z.union([
|
|
z.strictObject({ state: z.literal("not_requested") }),
|
|
z.strictObject({
|
|
state: z.literal("unavailable"),
|
|
message: z.string().min(1),
|
|
}),
|
|
z.strictObject({
|
|
state: z.literal("captured"),
|
|
items: z.array(
|
|
z.strictObject({
|
|
name: z.string().min(1),
|
|
value: z.string().nullable(),
|
|
redacted: z.boolean(),
|
|
}),
|
|
),
|
|
}),
|
|
]);
|
|
|
|
export type BrowserNetworkContentSelection = z.infer<
|
|
typeof browserNetworkContentSelectionSchema
|
|
>;
|
|
export type BrowserNetworkBody = z.infer<typeof browserNetworkBodySchema>;
|
|
export type BrowserNetworkHeaders = z.infer<typeof browserNetworkHeadersSchema>;
|