@bjoernrochel/sevro 0.1.0-rc.7 → 1.0.0
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/CONTRIBUTING.md +22 -6
- package/README.md +5 -1
- package/docs/README.md +19 -15
- package/docs/architecture.md +138 -17
- package/docs/assets/README.md +5 -4
- package/docs/builtin-graders-v1.md +17 -3
- package/docs/codex-host.md +155 -49
- package/docs/contracts.md +2 -1
- package/docs/creating-cases.md +9 -2
- package/docs/development-cli.md +187 -19
- package/docs/documentation-quality.md +12 -2
- package/docs/evaluation-workflow.md +110 -0
- package/docs/extension-protocol-v1.md +26 -3
- package/docs/getting-started.md +23 -2
- package/docs/guide-evaluation.md +33 -1
- package/docs/identity-v1.md +25 -2
- package/docs/installing.md +11 -11
- package/docs/native-controls.md +76 -5
- package/docs/native-hosts.md +196 -7
- package/docs/reading-results.md +102 -0
- package/docs/recipes.md +489 -0
- package/docs/releases.md +31 -12
- package/docs/report-v1.md +29 -1
- package/docs/results-v1.md +141 -4
- package/docs/run-settings-v1.md +147 -0
- package/docs/runtime-v1.md +307 -18
- package/docs/troubleshooting.md +12 -12
- package/docs/typescript-quality.md +89 -26
- package/examples/recipes/candidate.ts +26 -0
- package/examples/recipes/cases/coding-change.json +21 -0
- package/examples/recipes/cases/host-tools.json +21 -0
- package/examples/recipes/cases/mise.json +21 -0
- package/examples/recipes/cases/node.json +21 -0
- package/examples/recipes/cases/python-uv.json +21 -0
- package/examples/recipes/cases/semantic.json +17 -0
- package/examples/recipes/coding-change/check.ts +23 -0
- package/examples/recipes/extension-artifacts.ts +65 -0
- package/examples/recipes/extension-methods.ts +174 -0
- package/examples/recipes/extension.ts +37 -0
- package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/README.md +3 -0
- package/examples/recipes/run-inputs.ts +188 -0
- package/examples/recipes/run-options.ts +131 -0
- package/examples/recipes/run.ts +63 -0
- package/examples/recipes/runtime/empty.json +1 -0
- package/examples/recipes/runtime/goals.json +1 -0
- package/examples/recipes/runtime/mise.json +6 -0
- package/examples/recipes/runtime/path.json +1 -0
- package/examples/recipes/runtime/plugins.json +4 -0
- package/examples/recipes/runtime/python-uv.json +10 -0
- package/examples/recipes/semantic-judge.ts +29 -0
- package/examples/templates/coding/adapter.ts +17 -0
- package/examples/templates/coding/case.json +15 -0
- package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
- package/examples/templates/repository-skill/extension-data.ts +64 -0
- package/examples/templates/repository-skill/extension.ts +31 -0
- package/examples/templates/response/adapter.ts +12 -0
- package/examples/templates/response/case.json +13 -0
- package/package.json +11 -5
- package/schemas/report-v1.schema.json +9 -0
- package/schemas/run-evidence-v1.schema.json +2 -1
- package/schemas/runtime-v1.schema.json +63 -7
- package/src/advisory.ts +2 -2
- package/src/candidate-artifacts.ts +206 -0
- package/src/candidate-codex-state.ts +185 -0
- package/src/case-validation.ts +73 -50
- package/src/cli/cancellation.ts +20 -0
- package/src/cli/command.ts +87 -0
- package/src/cli/completion.ts +15 -0
- package/src/cli/demo.ts +60 -0
- package/src/cli/evaluation.ts +55 -0
- package/src/cli/extension.ts +79 -0
- package/src/cli/help.ts +198 -0
- package/src/cli/hosts.ts +55 -0
- package/src/cli/init-settings.ts +30 -0
- package/src/cli/init.ts +111 -0
- package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
- package/src/cli/loading.ts +151 -0
- package/src/cli/preparation.ts +154 -0
- package/src/cli/presentation.ts +93 -0
- package/src/cli/progress.ts +6 -0
- package/src/cli/run-manifest.ts +128 -0
- package/src/cli/run-options.ts +403 -0
- package/src/cli/run-overrides.ts +69 -0
- package/src/cli/settings.ts +154 -0
- package/src/cli/summary.ts +43 -0
- package/src/cli.ts +13 -591
- package/src/contract-types.ts +94 -0
- package/src/engine.ts +13 -5
- package/src/evaluation/advisory.ts +150 -0
- package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
- package/src/evaluation/collection.ts +88 -0
- package/src/evaluation/context.ts +10 -0
- package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
- package/src/evaluation/grading.ts +235 -0
- package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
- package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
- package/src/evaluation/progress.ts +19 -0
- package/src/evaluation/resources.ts +241 -0
- package/src/evaluation/run.ts +206 -0
- package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
- package/src/evaluation/trial.ts +800 -0
- package/src/evaluation/types.ts +207 -0
- package/src/evidence/host-results.ts +85 -0
- package/src/evidence/retained-artifacts.ts +18 -0
- package/src/evidence/trial-writer.ts +203 -0
- package/src/evidence/usage.ts +17 -0
- package/src/extension-client.ts +34 -96
- package/src/extension-session.ts +24 -48
- package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
- package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
- package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
- package/src/fixtures/create.ts +183 -0
- package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
- package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
- package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
- package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
- package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
- package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
- package/src/fixtures/workspace-state.ts +212 -0
- package/src/generated/cli-result.d.cts +2 -1
- package/src/generated/extension.d.cts +2 -1
- package/src/generated/report.cjs +1 -1
- package/src/generated/report.d.cts +2 -1
- package/src/generated/run-evidence.cjs +1 -1
- package/src/generated/run-evidence.d.cts +2 -1
- package/src/generated/runtime.cjs +1 -1
- package/src/generated/runtime.d.cts +2 -1
- package/src/git.ts +93 -0
- package/src/graders/git-head.ts +29 -84
- package/src/graders/shell.ts +57 -68
- package/src/host-diagnostic-redaction.ts +52 -0
- package/src/host-diagnostics.ts +331 -0
- package/src/hosts/claude-continuation.ts +57 -40
- package/src/hosts/claude-credential.ts +160 -63
- package/src/hosts/claude-events.ts +34 -18
- package/src/hosts/claude-keychain.ts +78 -21
- package/src/hosts/claude-login-recovery.ts +85 -0
- package/src/hosts/claude-login-refresh.ts +178 -0
- package/src/hosts/claude-login-store.ts +162 -0
- package/src/hosts/claude-login.ts +107 -0
- package/src/hosts/claude-native-goal.ts +15 -12
- package/src/hosts/claude-process.ts +10 -50
- package/src/hosts/claude-refresh-lock.ts +81 -0
- package/src/hosts/claude-repository-invocation.ts +9 -34
- package/src/hosts/claude-settings.ts +91 -14
- package/src/hosts/claude-skill-activations.ts +169 -0
- package/src/hosts/claude-state.ts +46 -16
- package/src/hosts/claude-tool-calls.ts +13 -10
- package/src/hosts/claude.ts +122 -73
- package/src/hosts/codex-app-server.ts +78 -52
- package/src/hosts/codex-configuration.ts +5 -10
- package/src/hosts/codex-exec-controls.ts +516 -0
- package/src/hosts/codex-marketplace.ts +12 -10
- package/src/hosts/codex-native-calls.ts +15 -122
- package/src/hosts/codex-process.ts +10 -54
- package/src/hosts/codex-profile.ts +50 -21
- package/src/hosts/codex-session-files.ts +1 -9
- package/src/hosts/codex-state.ts +141 -19
- package/src/hosts/codex.ts +221 -179
- package/src/hosts/isolation-common.ts +15 -11
- package/src/hosts/isolation-roots.ts +13 -20
- package/src/hosts/linux-sandbox.ts +27 -3
- package/src/hosts/mac-sandbox.ts +111 -18
- package/src/hosts/native-controls.ts +72 -22
- package/src/hosts/native-request.ts +55 -0
- package/src/hosts/process-lifecycle.ts +221 -45
- package/src/hosts/protected-worktrees.ts +1 -1
- package/src/hosts/skill-hook-catalog.ts +131 -0
- package/src/hosts/skill-hook-handler.ts +374 -0
- package/src/hosts/skill-hooks.ts +577 -0
- package/src/instrumentation.ts +7 -8
- package/src/native-transcript-state.ts +22 -3
- package/src/native-transcript-view.ts +52 -13
- package/src/native-transcripts.ts +34 -15
- package/src/paths.ts +22 -0
- package/src/process-lifecycle.ts +150 -0
- package/src/provenance.ts +27 -7
- package/src/prune.ts +300 -0
- package/src/report.ts +63 -36
- package/src/resolved-case.ts +1 -1
- package/src/results-lock.ts +42 -0
- package/src/results.ts +14 -18
- package/src/run-owner.ts +4 -4
- package/src/{runtime-config.ts → runtime/config.ts} +182 -44
- package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
- package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
- package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
- package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
- package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
- package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
- package/src/{runtime-query.ts → runtime/query.ts} +5 -5
- package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
- package/src/runtime/shared-cache.ts +130 -0
- package/src/{runtime-state.ts → runtime/state.ts} +28 -11
- package/src/schema-types.ts +73 -161
- package/src/shell.ts +4 -0
- package/src/transcript-retention.ts +146 -0
- package/src/value-guards.ts +22 -0
- package/docs/assets/sevro-logo.png +0 -0
- package/docs/assets/sevro-symbol.svg +0 -1
- package/docs/assets/sevro-wordmark.svg +0 -1
- package/docs/cache-ownership-validation.md +0 -49
- package/docs/claude-continuation-validation.md +0 -47
- package/docs/concurrency-validation.md +0 -86
- package/docs/configuration-root-validation.md +0 -70
- package/docs/engine-slice.md +0 -114
- package/docs/evidence/runtime-startup-30/README.md +0 -185
- package/docs/evidence/runtime-startup-30/after.json +0 -319
- package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
- package/docs/evidence/runtime-startup-30/before.json +0 -403
- package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
- package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
- package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
- package/docs/evidence.md +0 -17
- package/docs/fixture-cleanup-validation.md +0 -70
- package/docs/guide-verification.json +0 -239
- package/src/evaluation-context.ts +0 -257
- package/src/evaluation-fixture.ts +0 -313
- package/src/evaluation-run.ts +0 -198
- package/src/evaluation-trial.ts +0 -946
- package/src/evaluation-types.ts +0 -149
- package/src/hosts/claude-nested-skills.ts +0 -272
- package/src/hosts/codex-command-output.ts +0 -510
- package/src/hosts/codex-executor-command.ts +0 -291
- package/src/hosts/codex-skill-reads.ts +0 -609
- /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InstrumentationCapability,
|
|
3
|
+
InstrumentationRequest,
|
|
4
|
+
} from "../instrumentation";
|
|
5
|
+
import type { GeneratedFixture } from "../fixtures/generated";
|
|
6
|
+
import type { PreparationSources } from "../fixtures/preparation";
|
|
7
|
+
import type { RepositoryFixture } from "../fixtures/repository";
|
|
8
|
+
import type { ExtensionCase } from "../extension-session";
|
|
9
|
+
import type { openExtensionSession } from "../extension-session";
|
|
10
|
+
import type {
|
|
11
|
+
Assessment,
|
|
12
|
+
CheckDeclaration,
|
|
13
|
+
DomainOutcome,
|
|
14
|
+
ObservationEvidence,
|
|
15
|
+
} from "../contract-types";
|
|
16
|
+
import type {
|
|
17
|
+
CliResultData,
|
|
18
|
+
CliResultDataCase,
|
|
19
|
+
CliResultDataTrial,
|
|
20
|
+
} from "../schema-types";
|
|
21
|
+
import type { RuntimePolicy } from "../runtime/config";
|
|
22
|
+
import type { RuntimeRole } from "../runtime/state";
|
|
23
|
+
import type { CandidateArtifactRoots } from "../candidate-artifacts";
|
|
24
|
+
|
|
25
|
+
export class EvaluationConfigurationError extends Error {
|
|
26
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
27
|
+
super(message, options);
|
|
28
|
+
this.name = "EvaluationConfigurationError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HostResult {
|
|
33
|
+
finalMessage: string | null;
|
|
34
|
+
complete: boolean;
|
|
35
|
+
executionFailed?: boolean;
|
|
36
|
+
/** Adapter-owned, bounded failure summary suitable for CLI diagnostics. */
|
|
37
|
+
failureMessage?: string;
|
|
38
|
+
processFailure?: {
|
|
39
|
+
phase: "isolation-preflight" | "executable-preflight" | "execution";
|
|
40
|
+
reason: "exit" | "timeout" | "cancelled";
|
|
41
|
+
timeoutMs: number;
|
|
42
|
+
};
|
|
43
|
+
artifacts?: { id: string; bytes: Uint8Array }[];
|
|
44
|
+
observations?: Omit<ObservationEvidence, "source">[];
|
|
45
|
+
actualCondition?: "passive" | "enforced" | "unknown";
|
|
46
|
+
appliedInstrumentation?: InstrumentationRequest[];
|
|
47
|
+
inputTokens?: number | null;
|
|
48
|
+
outputTokens?: number | null;
|
|
49
|
+
costUsd?: number | null;
|
|
50
|
+
usageComplete?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** A failed host call can still provide bounded evidence for retention. */
|
|
54
|
+
export class HostExecutionFailure extends Error {
|
|
55
|
+
readonly result: HostResult;
|
|
56
|
+
constructor(
|
|
57
|
+
message: string | undefined,
|
|
58
|
+
result: HostResult,
|
|
59
|
+
options?: ErrorOptions,
|
|
60
|
+
) {
|
|
61
|
+
super(message, options);
|
|
62
|
+
this.result = { ...result, executionFailed: true, failureMessage: message };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class TrialPersistenceError extends Error {
|
|
67
|
+
constructor(workspace: string, options?: ErrorOptions) {
|
|
68
|
+
super(
|
|
69
|
+
`trial persistence failed; fixture retained at ${workspace}`,
|
|
70
|
+
options,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface HostAdapter {
|
|
76
|
+
id: string;
|
|
77
|
+
model: string;
|
|
78
|
+
effort: string;
|
|
79
|
+
instrumentation?: InstrumentationCapability[];
|
|
80
|
+
hostCapabilities?: string[];
|
|
81
|
+
configuration?: Record<string, unknown>;
|
|
82
|
+
run(request: {
|
|
83
|
+
/** Persist private diagnostic bytes before the native host removes its state. */
|
|
84
|
+
retainHostDiagnostics?: (bytes: Uint8Array) => Promise<void>;
|
|
85
|
+
runtimePolicy?: RuntimePolicy;
|
|
86
|
+
runtimeRole?: RuntimeRole;
|
|
87
|
+
candidateTranscriptRoot?: string;
|
|
88
|
+
candidateRuntimeRoot?: string;
|
|
89
|
+
candidateArtifactRoots?: CandidateArtifactRoots;
|
|
90
|
+
prompt: string;
|
|
91
|
+
followUpPrompt?: string;
|
|
92
|
+
workspace: string;
|
|
93
|
+
condition: "passive" | "enforced";
|
|
94
|
+
fixtureBinDir?: string;
|
|
95
|
+
instrumentation?: InstrumentationRequest[];
|
|
96
|
+
codexMarketplace?: {
|
|
97
|
+
artifactRoot: string;
|
|
98
|
+
marketplaceName: string;
|
|
99
|
+
pluginNames: string[];
|
|
100
|
+
artifactPaths: string[];
|
|
101
|
+
};
|
|
102
|
+
claudePluginDirs?: {
|
|
103
|
+
artifactRoots: string[];
|
|
104
|
+
artifactPaths: string[];
|
|
105
|
+
};
|
|
106
|
+
explicitSkillInvocation?:
|
|
107
|
+
| {
|
|
108
|
+
scope?: "plugin";
|
|
109
|
+
pluginName: string;
|
|
110
|
+
skillName: string;
|
|
111
|
+
token: string;
|
|
112
|
+
}
|
|
113
|
+
| {
|
|
114
|
+
scope: "repository";
|
|
115
|
+
pluginName?: never;
|
|
116
|
+
skillName: string;
|
|
117
|
+
token: string;
|
|
118
|
+
};
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
}): Promise<HostResult>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ResolvedCase {
|
|
124
|
+
id: string;
|
|
125
|
+
prompt: string;
|
|
126
|
+
followUpPrompt?: string;
|
|
127
|
+
fixture:
|
|
128
|
+
| { files: Record<string, string>; sourceRef?: never }
|
|
129
|
+
| Omit<RepositoryFixture, "kind">
|
|
130
|
+
| (GeneratedFixture & { sourceRef?: never });
|
|
131
|
+
checks: CheckDeclaration[];
|
|
132
|
+
requiredEvidence: string[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface EvaluationProgress {
|
|
136
|
+
stage: "preparation" | "candidate" | "grading" | "evidence";
|
|
137
|
+
status: "started" | "completed" | "skipped";
|
|
138
|
+
trial?: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface EvaluationOptions {
|
|
142
|
+
/** Optional best-effort observation; exceptions never change the evaluation. */
|
|
143
|
+
onProgress?: (event: EvaluationProgress) => void;
|
|
144
|
+
/** Redacted operator preview, retained for reproduction outside semantic identity. */
|
|
145
|
+
resolvedRunSettings?: Record<string, unknown>;
|
|
146
|
+
/** Additional non-secret assessment settings included in configuration identity. */
|
|
147
|
+
assessmentSettings?: Record<string, unknown>;
|
|
148
|
+
runtimePolicy?: RuntimePolicy;
|
|
149
|
+
projectRoot: string;
|
|
150
|
+
resultsRoot: string;
|
|
151
|
+
runStateRoot?: string;
|
|
152
|
+
case: ResolvedCase;
|
|
153
|
+
host: HostAdapter;
|
|
154
|
+
semanticHost?: HostAdapter;
|
|
155
|
+
advisoryHost?: HostAdapter;
|
|
156
|
+
advisoryExcludedPaths?: string[];
|
|
157
|
+
runnerBuildDigest: string;
|
|
158
|
+
runnerCheckoutRoot?: string;
|
|
159
|
+
projectDigest: string;
|
|
160
|
+
condition: "passive" | "enforced";
|
|
161
|
+
trialCount: number;
|
|
162
|
+
jobs?: number;
|
|
163
|
+
passThreshold: number;
|
|
164
|
+
dry?: boolean;
|
|
165
|
+
signal?: AbortSignal;
|
|
166
|
+
extension?: {
|
|
167
|
+
session: Awaited<ReturnType<typeof openExtensionSession>>;
|
|
168
|
+
resolvedCase: ExtensionCase;
|
|
169
|
+
};
|
|
170
|
+
preparationSources?: PreparationSources;
|
|
171
|
+
shellIsolation?: {
|
|
172
|
+
protectedRoots: string[];
|
|
173
|
+
toolchainBinDir?: string;
|
|
174
|
+
uvRuntimeCache?: boolean;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Persisted evaluations always retain a trial path and a known outcome list. */
|
|
179
|
+
export interface TrialSummary
|
|
180
|
+
extends
|
|
181
|
+
Assessment,
|
|
182
|
+
Omit<
|
|
183
|
+
CliResultDataTrial,
|
|
184
|
+
keyof Assessment | "domainOutcomes" | "artifactPath"
|
|
185
|
+
> {
|
|
186
|
+
domainOutcomes: DomainOutcome[];
|
|
187
|
+
artifactPath: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface CaseSummary
|
|
191
|
+
extends Assessment, Omit<CliResultDataCase, keyof Assessment | "trials"> {
|
|
192
|
+
trials: TrialSummary[];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Unlike pre-run wire failures, a returned evaluation has run/evidence paths. */
|
|
196
|
+
export interface CliResult
|
|
197
|
+
extends
|
|
198
|
+
Assessment,
|
|
199
|
+
Omit<
|
|
200
|
+
CliResultData,
|
|
201
|
+
keyof Assessment | "runId" | "exitCode" | "evidencePath" | "cases"
|
|
202
|
+
> {
|
|
203
|
+
runId: string;
|
|
204
|
+
exitCode: number;
|
|
205
|
+
evidencePath: string;
|
|
206
|
+
cases: CaseSummary[];
|
|
207
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isRecord, validQualifiedId as validEvidenceId } from "../value-guards";
|
|
2
|
+
import { canonicalJson } from "../identity";
|
|
3
|
+
import type { HostResult } from "../evaluation/types";
|
|
4
|
+
import type {
|
|
5
|
+
ObservationCompleteness,
|
|
6
|
+
ObservationEvidence,
|
|
7
|
+
} from "../contract-types";
|
|
8
|
+
|
|
9
|
+
export function hostObservations(
|
|
10
|
+
result: HostResult,
|
|
11
|
+
hostId: string,
|
|
12
|
+
existingIds: Set<string>,
|
|
13
|
+
): ObservationEvidence[] {
|
|
14
|
+
const items = result.observations ?? [];
|
|
15
|
+
if (items.length > 128) throw new Error("too many host observations");
|
|
16
|
+
const ids = new Set([...existingIds, "sevro.observation.final-message"]);
|
|
17
|
+
let totalBytes = 0;
|
|
18
|
+
return items.map((value: unknown) => {
|
|
19
|
+
const item = validatedHostObservation(value, ids);
|
|
20
|
+
ids.add(item.id);
|
|
21
|
+
const data = canonicalJson(item.data);
|
|
22
|
+
totalBytes += Buffer.byteLength(data, "utf8");
|
|
23
|
+
if (totalBytes > 8 * 1024 * 1024)
|
|
24
|
+
throw new Error("host observations exceed 8 MiB");
|
|
25
|
+
return {
|
|
26
|
+
id: item.id,
|
|
27
|
+
source: hostId,
|
|
28
|
+
completeness: item.completeness,
|
|
29
|
+
data: JSON.parse(data) as Record<string, unknown>,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function hostArtifacts(result: HostResult, existingIds: Set<string>) {
|
|
35
|
+
const items = result.artifacts ?? [];
|
|
36
|
+
if (!Array.isArray(items) || items.length > 32)
|
|
37
|
+
throw new Error("too many host artifacts");
|
|
38
|
+
const ids = new Set(existingIds);
|
|
39
|
+
let totalBytes = 0;
|
|
40
|
+
return items.map((value: unknown) => {
|
|
41
|
+
const item = validatedHostArtifact(value, ids);
|
|
42
|
+
ids.add(item.id);
|
|
43
|
+
totalBytes += item.bytes.byteLength;
|
|
44
|
+
if (
|
|
45
|
+
item.bytes.byteLength > 8 * 1024 * 1024 ||
|
|
46
|
+
totalBytes > 32 * 1024 * 1024
|
|
47
|
+
)
|
|
48
|
+
throw new Error("host artifacts exceed the size limit");
|
|
49
|
+
return { id: item.id, bytes: Buffer.from(item.bytes) };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validObservationId(value: unknown, ids: Set<string>): value is string {
|
|
54
|
+
return (
|
|
55
|
+
validEvidenceId(value) &&
|
|
56
|
+
!value.startsWith("sevro.observation.") &&
|
|
57
|
+
!ids.has(value)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validCompleteness(value: unknown): value is ObservationCompleteness {
|
|
62
|
+
return value === "complete" || value === "partial" || value === "unavailable";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validatedHostObservation(value: unknown, ids: Set<string>) {
|
|
66
|
+
if (!isRecord(value)) throw new Error("invalid host observation");
|
|
67
|
+
if (
|
|
68
|
+
!validObservationId(value.id, ids) ||
|
|
69
|
+
!validCompleteness(value.completeness) ||
|
|
70
|
+
!isRecord(value.data)
|
|
71
|
+
)
|
|
72
|
+
throw new Error("invalid host observation");
|
|
73
|
+
return { id: value.id, completeness: value.completeness, data: value.data };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validArtifactId(value: unknown, ids: Set<string>): value is string {
|
|
77
|
+
return validEvidenceId(value) && !ids.has(value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function validatedHostArtifact(value: unknown, ids: Set<string>) {
|
|
81
|
+
if (!isRecord(value)) throw new Error("invalid host artifact");
|
|
82
|
+
if (!validArtifactId(value.id, ids) || !(value.bytes instanceof Uint8Array))
|
|
83
|
+
throw new Error("invalid host artifact");
|
|
84
|
+
return { id: value.id, bytes: value.bytes };
|
|
85
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { sha256 } from "../identity";
|
|
4
|
+
|
|
5
|
+
export async function verifyRetainedArtifacts(
|
|
6
|
+
artifacts: { path: string; sha256: string }[],
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
for (const artifact of artifacts) {
|
|
9
|
+
let actual: string;
|
|
10
|
+
try {
|
|
11
|
+
actual = sha256(await readFile(fileURLToPath(artifact.path)));
|
|
12
|
+
} catch {
|
|
13
|
+
throw new Error("retained preparation artifact is unreadable");
|
|
14
|
+
}
|
|
15
|
+
if (actual !== artifact.sha256)
|
|
16
|
+
throw new Error("retained preparation artifact changed");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { TrialPersistenceError, type TrialSummary } from "../evaluation/types";
|
|
5
|
+
import { sha256 } from "../identity";
|
|
6
|
+
import type { Assessment, CheckOutcome } from "../results";
|
|
7
|
+
import type {
|
|
8
|
+
ArtifactReference,
|
|
9
|
+
Diagnostic,
|
|
10
|
+
ObservationEvidence,
|
|
11
|
+
RawResultReference,
|
|
12
|
+
} from "../contract-types";
|
|
13
|
+
import type { RunEvidenceDataTrialEvidence } from "../schema-types";
|
|
14
|
+
import { atomicWriteJson } from "../storage";
|
|
15
|
+
import type { hostArtifacts } from "./host-results";
|
|
16
|
+
import { verifyRetainedArtifacts } from "./retained-artifacts";
|
|
17
|
+
|
|
18
|
+
type TrialEvidence = RunEvidenceDataTrialEvidence;
|
|
19
|
+
export type TrialObservation = ObservationEvidence;
|
|
20
|
+
|
|
21
|
+
interface TrialWriterInput {
|
|
22
|
+
readonly runDir: string;
|
|
23
|
+
readonly trial: number;
|
|
24
|
+
readonly workspace: string;
|
|
25
|
+
readonly preparationArtifacts: readonly ArtifactReference[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Owns private trial files and their references, independently of run collection. */
|
|
29
|
+
export class TrialEvidenceWriter {
|
|
30
|
+
private readonly references: ArtifactReference[];
|
|
31
|
+
private rawPath: string | null = null;
|
|
32
|
+
private rawDigest: string | null = null;
|
|
33
|
+
|
|
34
|
+
constructor(private readonly input: TrialWriterInput) {
|
|
35
|
+
this.references = [...input.preparationArtifacts];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get artifactRefs(): ArtifactReference[] {
|
|
39
|
+
return [...this.references];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get hasCandidateMessage(): boolean {
|
|
43
|
+
return this.rawDigest !== null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async writePrivateFile(
|
|
47
|
+
path: string,
|
|
48
|
+
bytes: string | Uint8Array,
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
try {
|
|
51
|
+
await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
|
|
52
|
+
} catch (cause) {
|
|
53
|
+
throw new TrialPersistenceError(this.input.workspace, { cause });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async retainReference(
|
|
58
|
+
id: string,
|
|
59
|
+
filename: string,
|
|
60
|
+
bytes: string | Uint8Array,
|
|
61
|
+
): Promise<ArtifactReference> {
|
|
62
|
+
const path = join(
|
|
63
|
+
this.input.runDir,
|
|
64
|
+
`trial-${this.input.trial}-${filename}`,
|
|
65
|
+
);
|
|
66
|
+
await this.writePrivateFile(path, bytes);
|
|
67
|
+
const reference = {
|
|
68
|
+
id,
|
|
69
|
+
path: pathToFileURL(path).href,
|
|
70
|
+
sha256: sha256(bytes),
|
|
71
|
+
};
|
|
72
|
+
this.references.push(reference);
|
|
73
|
+
return reference;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async retainArtifacts(
|
|
77
|
+
artifacts: readonly ReturnType<typeof hostArtifacts>[number][],
|
|
78
|
+
prefix = "",
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
for (const artifact of artifacts)
|
|
81
|
+
await this.retainReference(
|
|
82
|
+
artifact.id,
|
|
83
|
+
`${prefix}${artifact.id}.bin`,
|
|
84
|
+
artifact.bytes,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
verifyArtifacts(): Promise<void> {
|
|
89
|
+
return verifyRetainedArtifacts(this.references);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
observeCandidate(
|
|
93
|
+
source: string,
|
|
94
|
+
result: { finalMessage: string | null; complete: boolean } | null,
|
|
95
|
+
): TrialObservation {
|
|
96
|
+
if (result === null || result.finalMessage == null) {
|
|
97
|
+
this.rawDigest = null;
|
|
98
|
+
return {
|
|
99
|
+
id: "sevro.observation.final-message",
|
|
100
|
+
source,
|
|
101
|
+
completeness: "unavailable",
|
|
102
|
+
data: {},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
this.rawDigest = sha256(result.finalMessage);
|
|
106
|
+
return {
|
|
107
|
+
id: "sevro.observation.final-message",
|
|
108
|
+
source,
|
|
109
|
+
completeness: result.complete ? "complete" : "partial",
|
|
110
|
+
data: {
|
|
111
|
+
sha256: this.rawDigest,
|
|
112
|
+
byteLength: Buffer.byteLength(result.finalMessage, "utf8"),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async retainSemanticVerdicts(
|
|
118
|
+
message: string | null,
|
|
119
|
+
suffix: string,
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
if (message === null || Buffer.byteLength(message, "utf8") > 1024 * 1024)
|
|
122
|
+
return;
|
|
123
|
+
await this.retainReference(
|
|
124
|
+
`sevro.semantic.verdicts${suffix}`,
|
|
125
|
+
`semantic-verdicts${suffix}.json`,
|
|
126
|
+
Buffer.from(message, "utf8"),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async retainAdvisoryResponse(
|
|
131
|
+
source: string,
|
|
132
|
+
message: string | null,
|
|
133
|
+
): Promise<RawResultReference> {
|
|
134
|
+
if (message === null || Buffer.byteLength(message, "utf8") > 64 * 1024)
|
|
135
|
+
return { source, path: null, sha256: null };
|
|
136
|
+
const reference = await this.retainReference(
|
|
137
|
+
"sevro.advisory.response",
|
|
138
|
+
"advisory.json",
|
|
139
|
+
message,
|
|
140
|
+
);
|
|
141
|
+
return { source, path: reference.path, sha256: reference.sha256 };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async retainRawCandidate(message: string | null | undefined): Promise<void> {
|
|
145
|
+
await this.verifyArtifacts();
|
|
146
|
+
if (message == null) {
|
|
147
|
+
this.rawPath = null;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
this.rawPath = join(this.input.runDir, `trial-${this.input.trial}-raw.txt`);
|
|
151
|
+
await this.writePrivateFile(this.rawPath, message);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
candidateRawResult(source: string): RawResultReference {
|
|
155
|
+
return {
|
|
156
|
+
source,
|
|
157
|
+
path: this.rawPath ? pathToFileURL(this.rawPath).href : null,
|
|
158
|
+
sha256: this.rawDigest,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
diagnosticLocation(diagnostic: Diagnostic | undefined) {
|
|
163
|
+
if (!diagnostic) return diagnostic;
|
|
164
|
+
const artifact = this.references.find((ref) =>
|
|
165
|
+
ref.id.endsWith("sevro.host.diagnostics"),
|
|
166
|
+
);
|
|
167
|
+
if (!artifact) return diagnostic;
|
|
168
|
+
const message = `${diagnostic.message}; diagnostics: ${artifact.path}`;
|
|
169
|
+
return message.length <= 4096 ? { ...diagnostic, message } : diagnostic;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async persistTrial(
|
|
173
|
+
caseId: string,
|
|
174
|
+
assessment: Assessment,
|
|
175
|
+
checks: CheckOutcome[],
|
|
176
|
+
domainOutcomes: TrialSummary["domainOutcomes"],
|
|
177
|
+
evidence: TrialEvidence,
|
|
178
|
+
): Promise<TrialSummary> {
|
|
179
|
+
const artifactPath = join(
|
|
180
|
+
this.input.runDir,
|
|
181
|
+
`trial-${this.input.trial}.json`,
|
|
182
|
+
);
|
|
183
|
+
const summary: TrialSummary = {
|
|
184
|
+
trial: this.input.trial,
|
|
185
|
+
...assessment,
|
|
186
|
+
checks,
|
|
187
|
+
domainOutcomes,
|
|
188
|
+
artifactPath,
|
|
189
|
+
};
|
|
190
|
+
try {
|
|
191
|
+
await atomicWriteJson(artifactPath, {
|
|
192
|
+
format: "sevro.trial-evidence.v1",
|
|
193
|
+
caseId,
|
|
194
|
+
trial: this.input.trial,
|
|
195
|
+
result: summary,
|
|
196
|
+
evidence,
|
|
197
|
+
});
|
|
198
|
+
} catch (cause) {
|
|
199
|
+
throw new TrialPersistenceError(this.input.workspace, { cause });
|
|
200
|
+
}
|
|
201
|
+
return summary;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HostResult } from "../evaluation/types";
|
|
2
|
+
import type { UsageEvidence } from "../contract-types";
|
|
3
|
+
|
|
4
|
+
export function usage(result: HostResult | null): UsageEvidence {
|
|
5
|
+
return result === null
|
|
6
|
+
? { inputTokens: null, outputTokens: null, costUsd: null, complete: false }
|
|
7
|
+
: availableUsage(result);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function availableUsage(result: HostResult): UsageEvidence {
|
|
11
|
+
return {
|
|
12
|
+
inputTokens: result.inputTokens ?? null,
|
|
13
|
+
outputTokens: result.outputTokens ?? null,
|
|
14
|
+
costUsd: result.costUsd ?? null,
|
|
15
|
+
complete: result.usageComplete ?? false,
|
|
16
|
+
};
|
|
17
|
+
}
|
package/src/extension-client.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import validExchange from "./generated/extension.cjs";
|
|
3
3
|
import { isRecord, isStringArray } from "./value-guards";
|
|
4
|
+
import {
|
|
5
|
+
captureBoundedBytes,
|
|
6
|
+
ProcessDeadline,
|
|
7
|
+
terminateOwnedProcessGroup,
|
|
8
|
+
} from "./process-lifecycle";
|
|
4
9
|
|
|
5
10
|
const MAX_MESSAGE_BYTES = 8 * 1024 * 1024;
|
|
6
11
|
const MAX_DIAGNOSTIC_BYTES = 64 * 1024;
|
|
@@ -35,61 +40,6 @@ export interface ExchangeOptions {
|
|
|
35
40
|
signal?: AbortSignal;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
function boundedChunk(
|
|
39
|
-
value: Uint8Array,
|
|
40
|
-
size: number,
|
|
41
|
-
limit: number,
|
|
42
|
-
truncate: boolean,
|
|
43
|
-
): Uint8Array {
|
|
44
|
-
if (!truncate && size + value.byteLength > limit)
|
|
45
|
-
throw new ExtensionProtocolError("extension message exceeds 8 MiB");
|
|
46
|
-
return value.subarray(0, Math.max(0, limit - size));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function readBounded(
|
|
50
|
-
stream: ReadableStream<Uint8Array>,
|
|
51
|
-
limit: number,
|
|
52
|
-
truncate: boolean,
|
|
53
|
-
): Promise<Uint8Array> {
|
|
54
|
-
const reader = stream.getReader();
|
|
55
|
-
const chunks: Uint8Array[] = [];
|
|
56
|
-
let size = 0;
|
|
57
|
-
for (;;) {
|
|
58
|
-
const { value, done } = await reader.read();
|
|
59
|
-
if (done) break;
|
|
60
|
-
const kept = boundedChunk(value, size, limit, truncate);
|
|
61
|
-
if (kept.byteLength) chunks.push(kept);
|
|
62
|
-
size += kept.byteLength;
|
|
63
|
-
}
|
|
64
|
-
return concatenateChunks(chunks, size);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function concatenateChunks(chunks: Uint8Array[], size: number): Uint8Array {
|
|
68
|
-
const result = new Uint8Array(size);
|
|
69
|
-
let offset = 0;
|
|
70
|
-
for (const chunk of chunks) {
|
|
71
|
-
result.set(chunk, offset);
|
|
72
|
-
offset += chunk.byteLength;
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function terminate(proc: Bun.Subprocess): void {
|
|
78
|
-
if (process.platform !== "win32") {
|
|
79
|
-
try {
|
|
80
|
-
process.kill(-proc.pid, "SIGKILL");
|
|
81
|
-
return;
|
|
82
|
-
} catch {
|
|
83
|
-
// The group may have exited while the runner was handling the error.
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
proc.kill("SIGKILL");
|
|
88
|
-
} catch {
|
|
89
|
-
// The process already exited.
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
43
|
interface SuccessfulExtensionResponse extends ExtensionResponse {
|
|
94
44
|
result: Record<string, unknown>;
|
|
95
45
|
error?: never;
|
|
@@ -217,13 +167,23 @@ function assertSuccessfulResponse(
|
|
|
217
167
|
}
|
|
218
168
|
|
|
219
169
|
class ExtensionExchange {
|
|
220
|
-
private
|
|
221
|
-
private cancel: (() => void) | undefined;
|
|
170
|
+
private readonly deadline: ProcessDeadline;
|
|
222
171
|
constructor(
|
|
223
172
|
private readonly proc: Bun.Subprocess,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
) {
|
|
173
|
+
options: ExchangeOptions,
|
|
174
|
+
timeoutMs: number,
|
|
175
|
+
) {
|
|
176
|
+
this.deadline = new ProcessDeadline({
|
|
177
|
+
timeoutMs,
|
|
178
|
+
signal: options.signal,
|
|
179
|
+
interruption: (reason) =>
|
|
180
|
+
new ExtensionProtocolError(
|
|
181
|
+
reason === "timeout"
|
|
182
|
+
? "extension timed out"
|
|
183
|
+
: "extension request cancelled",
|
|
184
|
+
),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
227
187
|
|
|
228
188
|
private async output(
|
|
229
189
|
request: ExtensionRequest,
|
|
@@ -232,9 +192,17 @@ class ExtensionExchange {
|
|
|
232
192
|
const stdin = extensionWriter(this.proc);
|
|
233
193
|
const { stdout, stderr } = extensionReaders(this.proc);
|
|
234
194
|
const write = writeExtensionRequest(stdin, payload);
|
|
235
|
-
const [bytes, , exitCode] = await Promise.all([
|
|
236
|
-
|
|
237
|
-
|
|
195
|
+
const [{ bytes }, , exitCode] = await Promise.all([
|
|
196
|
+
captureBoundedBytes(stdout, {
|
|
197
|
+
limit: MAX_MESSAGE_BYTES,
|
|
198
|
+
overflow: "reject",
|
|
199
|
+
exceeded: () =>
|
|
200
|
+
new ExtensionProtocolError("extension message exceeds 8 MiB"),
|
|
201
|
+
}),
|
|
202
|
+
captureBoundedBytes(stderr, {
|
|
203
|
+
limit: MAX_DIAGNOSTIC_BYTES,
|
|
204
|
+
overflow: "truncate",
|
|
205
|
+
}),
|
|
238
206
|
this.proc.exited,
|
|
239
207
|
write,
|
|
240
208
|
]);
|
|
@@ -248,48 +216,18 @@ class ExtensionExchange {
|
|
|
248
216
|
return response;
|
|
249
217
|
}
|
|
250
218
|
|
|
251
|
-
private timeout(): Promise<never> {
|
|
252
|
-
return new Promise((_resolve, reject) => {
|
|
253
|
-
this.timer = setTimeout(() => {
|
|
254
|
-
reject(new ExtensionProtocolError("extension timed out"));
|
|
255
|
-
}, this.timeoutMs);
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private aborted(): Promise<never> {
|
|
260
|
-
return new Promise((_resolve, reject) => {
|
|
261
|
-
const signal = this.options.signal;
|
|
262
|
-
if (!signal) return;
|
|
263
|
-
this.cancel = () => {
|
|
264
|
-
reject(new ExtensionProtocolError("extension request cancelled"));
|
|
265
|
-
};
|
|
266
|
-
if (signal.aborted) this.cancel();
|
|
267
|
-
else signal.addEventListener("abort", this.cancel, { once: true });
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
private dispose(): void {
|
|
272
|
-
if (this.timer) clearTimeout(this.timer);
|
|
273
|
-
if (this.cancel)
|
|
274
|
-
this.options.signal?.removeEventListener("abort", this.cancel);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
219
|
async run(
|
|
278
220
|
request: ExtensionRequest,
|
|
279
221
|
payload: Uint8Array,
|
|
280
222
|
): Promise<SuccessfulExtensionResponse> {
|
|
281
223
|
try {
|
|
282
|
-
return await
|
|
283
|
-
this.output(request, payload),
|
|
284
|
-
this.timeout(),
|
|
285
|
-
this.aborted(),
|
|
286
|
-
]);
|
|
224
|
+
return await this.deadline.wait(this.output(request, payload));
|
|
287
225
|
} catch (cause) {
|
|
288
|
-
|
|
226
|
+
terminateOwnedProcessGroup(this.proc);
|
|
289
227
|
if (cause instanceof ExtensionProtocolError) throw cause;
|
|
290
228
|
throw new ExtensionProtocolError("extension transport failed", { cause });
|
|
291
229
|
} finally {
|
|
292
|
-
this.dispose();
|
|
230
|
+
this.deadline.dispose();
|
|
293
231
|
}
|
|
294
232
|
}
|
|
295
233
|
}
|