refactor(observation): share web runtime partial retention

Observe and inspect used the same promote-or-keep path. One helper
owns that sequence so the two operations cannot drift.

Co-authored-by: morluto <morluto@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-10-20 19:50:03 +00:00
parent fd1ed0cf7b
commit c2cac9bfe2
+47 -58
View File
@@ -41,36 +41,19 @@ export class WebRuntimeService {
);
if (!parsed.ok) return parsed;
const result = await this.provider.observeExecution(parsed.value, options);
if (!result.ok) {
const partial = result.error.partialObservation;
if (
partial !== undefined &&
!("evidence_id" in partial) &&
"instrumentation" in partial &&
"window" in partial
) {
const verifiedPartial = webExecutionSchema.safeParse(partial);
if (verifiedPartial.success) {
const evidence = this.evidence(
operation,
parsed.value,
verifiedPartial.data,
);
if (evidence.ok) {
result.error.partialObservation = evidence.value;
return result;
}
if (result.error instanceof AnalysisCancelledError) return evidence;
}
}
return err(
keepIncompleteObservation(
result.error,
operation,
"Provider retained a non-JSON incomplete execution observation.",
),
if (!result.ok)
return this.retainRuntimePartial(
result.error,
operation,
"Provider retained a non-JSON incomplete execution observation.",
(partial) => {
if (!("instrumentation" in partial) || !("window" in partial))
return undefined;
const verified = webExecutionSchema.safeParse(partial);
if (!verified.success) return undefined;
return this.evidence(operation, parsed.value, verified.data);
},
);
}
const verified = webExecutionSchema.safeParse(result.value);
if (!verified.success)
return err(new AnalysisOutputError(operation, verified.error.message));
@@ -104,36 +87,19 @@ export class WebRuntimeService {
parsed.value,
options,
);
if (!result.ok) {
const partial = result.error.partialObservation;
if (
partial !== undefined &&
!("evidence_id" in partial) &&
"selected_node" in partial &&
"listeners" in partial
) {
const verifiedPartial = webEventListenersSchema.safeParse(partial);
if (verifiedPartial.success) {
const evidence = this.evidence(
operation,
parsed.value,
verifiedPartial.data,
);
if (evidence.ok) {
result.error.partialObservation = evidence.value;
return result;
}
if (result.error instanceof AnalysisCancelledError) return evidence;
}
}
return err(
keepIncompleteObservation(
result.error,
operation,
"Provider retained a non-JSON incomplete event-listener observation.",
),
if (!result.ok)
return this.retainRuntimePartial(
result.error,
operation,
"Provider retained a non-JSON incomplete event-listener observation.",
(partial) => {
if (!("selected_node" in partial) || !("listeners" in partial))
return undefined;
const verified = webEventListenersSchema.safeParse(partial);
if (!verified.success) return undefined;
return this.evidence(operation, parsed.value, verified.data);
},
);
}
const verified = webEventListenersSchema.safeParse(result.value);
if (!verified.success)
return err(new AnalysisOutputError(operation, verified.error.message));
@@ -150,6 +116,29 @@ export class WebRuntimeService {
return this.evidence(operation, parsed.value, verified.data, options);
}
/** Promote a schema-valid partial, or keep a JSON snapshot of an incomplete one. */
private retainRuntimePartial(
error: AnalysisError,
operation: string,
nonJsonReason: string,
promote: (
partial: NonNullable<AnalysisError["partialObservation"]>,
) => Result<Evidence, AnalysisError> | undefined,
): Result<Evidence, AnalysisError> {
const partial = error.partialObservation;
if (partial !== undefined && !("evidence_id" in partial)) {
const evidence = promote(partial);
if (evidence !== undefined) {
if (evidence.ok) {
error.partialObservation = evidence.value;
return err(error);
}
if (error instanceof AnalysisCancelledError) return evidence;
}
}
return err(keepIncompleteObservation(error, operation, nonJsonReason));
}
private evidence(
operation: string,
input: