@loucompanion/forge-bridge 0.1.3-dev.fbe15023cdfe → 0.1.4-dev.9445952e33ba
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/README.md +88 -2
- package/dist/bridge/bridge-chat.types.d.ts +111 -0
- package/dist/bridge/bridge-chat.types.js +401 -0
- package/dist/bridge/bridge.base.d.ts +4 -1
- package/dist/bridge/bridge.service.d.ts +72 -3
- package/dist/bridge/bridge.service.js +1563 -152
- package/dist/bridge/bridge.types.d.ts +46 -1
- package/dist/bridge/dispatch-result-outbox.d.ts +25 -0
- package/dist/bridge/dispatch-result-outbox.js +62 -0
- package/dist/bridge/dispatch-result-worker.d.ts +2 -0
- package/dist/bridge/dispatch-result-worker.js +122 -0
- package/dist/bridge/internals.d.ts +2 -0
- package/dist/bridge/internals.js +1 -0
- package/dist/bridge/readiness-result-outbox.d.ts +21 -0
- package/dist/bridge/readiness-result-outbox.js +54 -0
- package/dist/bridge/readiness-result-worker.d.ts +2 -0
- package/dist/bridge/readiness-result-worker.js +121 -0
- package/dist/bridge/runner-host-capabilities.d.ts +15 -0
- package/dist/bridge/runner-host-capabilities.js +93 -0
- package/dist/cleanup/cleanup.service.d.ts +1 -0
- package/dist/cleanup/cleanup.service.js +70 -9
- package/dist/cli/cli.help.js +2 -2
- package/dist/cli/cli.service.d.ts +14 -6
- package/dist/cli/cli.service.js +75 -16
- package/dist/cli/local-config.d.ts +1 -0
- package/dist/cli/local-config.js +4 -0
- package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
- package/dist/runtime/aspnet-runtime-prebuild.js +341 -0
- package/dist/runtime/attempt-database-broker.d.ts +89 -0
- package/dist/runtime/attempt-database-broker.js +390 -0
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +2 -1
- package/dist/runtime/attempt-node-runtime-launcher.js +57 -18
- package/dist/runtime/attempt-path-operation.d.ts +21 -0
- package/dist/runtime/attempt-path-operation.js +94 -0
- package/dist/runtime/attempt-private-directory.d.ts +6 -0
- package/dist/runtime/attempt-private-directory.js +54 -0
- package/dist/runtime/attempt-process-supervisor.d.ts +11 -0
- package/dist/runtime/attempt-process-supervisor.js +67 -20
- package/dist/runtime/canonical-source-file.d.ts +3 -0
- package/dist/runtime/canonical-source-file.js +54 -0
- package/dist/runtime/docker-engine-probe.d.ts +90 -0
- package/dist/runtime/docker-engine-probe.js +333 -0
- package/dist/runtime/generated-output-reset.d.ts +10 -0
- package/dist/runtime/generated-output-reset.js +62 -8
- package/dist/runtime/host-database-capability-boundary.d.ts +16 -0
- package/dist/runtime/host-database-capability-boundary.js +62 -0
- package/dist/runtime/host-diagnostics.d.ts +11 -0
- package/dist/runtime/host-diagnostics.js +191 -0
- package/dist/runtime/host-execution-root.d.ts +4 -0
- package/dist/runtime/host-execution-root.js +39 -0
- package/dist/runtime/host-listener-inspector.d.ts +8 -1
- package/dist/runtime/host-listener-inspector.js +121 -60
- package/dist/runtime/host-runtime-contract.d.ts +12 -1
- package/dist/runtime/host-runtime-contract.js +168 -55
- package/dist/runtime/host-runtime-lease.d.ts +92 -8
- package/dist/runtime/host-runtime-lease.js +1141 -114
- package/dist/runtime/host-runtime-promotion.d.ts +12 -3
- package/dist/runtime/host-runtime-promotion.js +59 -12
- package/dist/runtime/memory-capacity.d.ts +70 -0
- package/dist/runtime/memory-capacity.js +123 -0
- package/dist/runtime/native-test-postgres-fallback.d.ts +25 -0
- package/dist/runtime/native-test-postgres-fallback.js +91 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +3 -0
- package/dist/runtime/node-host-runtime-adapter.js +28 -11
- package/dist/runtime/pinned-directory-process.d.ts +9 -0
- package/dist/runtime/pinned-directory-process.js +61 -0
- package/dist/runtime/pinned-source-directory.d.ts +17 -0
- package/dist/runtime/pinned-source-directory.js +94 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +1 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +53 -5
- package/dist/runtime/prepared-runtime-identity.d.ts +6 -0
- package/dist/runtime/prepared-runtime-identity.js +218 -0
- package/dist/runtime/prepared-runtime-readiness.d.ts +30 -0
- package/dist/runtime/prepared-runtime-readiness.js +285 -0
- package/dist/runtime/runtime-isolation-lease.js +4 -1
- package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
- package/dist/runtime/workspace-resource-estimator.js +174 -0
- package/dist/sandbox/preflight.d.ts +31 -1
- package/dist/sandbox/preflight.js +237 -51
- package/dist/sandbox/repository-lock.d.ts +2 -1
- package/dist/sandbox/repository-lock.js +17 -8
- package/dist/sandbox/runtime.d.ts +6 -1
- package/dist/sandbox/runtime.js +132 -103
- package/dist/sandbox/sandbox.types.d.ts +7 -0
- package/dist/sandbox/workspace.service.js +2 -0
- package/dist/session/managed-attempt-tools.d.ts +17 -1
- package/dist/session/managed-attempt-tools.js +485 -31
- package/dist/session/managed-build-coordinator.d.ts +5 -0
- package/dist/session/managed-build-coordinator.js +105 -0
- package/dist/session/managed-build-receipt.d.ts +21 -0
- package/dist/session/managed-build-receipt.js +337 -0
- package/dist/session/managed-verification-timeouts.d.ts +8 -0
- package/dist/session/managed-verification-timeouts.js +17 -0
- package/dist/session/phase-completion.artifacts.d.ts +17 -0
- package/dist/session/phase-completion.artifacts.js +224 -22
- package/dist/session/phase-completion.d.ts +11 -1
- package/dist/session/phase-completion.helpers.d.ts +5 -0
- package/dist/session/phase-completion.helpers.js +30 -7
- package/dist/session/phase-completion.js +14 -3
- package/dist/session/phase-completion.prompts.js +17 -18
- package/dist/session/phase-completion.runtime.d.ts +3 -1
- package/dist/session/phase-completion.runtime.js +126 -17
- package/dist/session/phase-completion.schemas.js +49 -2
- package/dist/session/phase-completion.validation.d.ts +1 -0
- package/dist/session/phase-completion.validation.js +111 -21
- package/dist/session/pre-publication-validation.d.ts +75 -0
- package/dist/session/pre-publication-validation.js +661 -0
- package/dist/session/provider-cli/bridge-cli.conformance-fixtures.d.ts +72 -0
- package/dist/session/provider-cli/bridge-cli.conformance-fixtures.js +462 -0
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +8 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +23 -3
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +4 -2
- package/dist/session/provider-cli/provider-cli.artifacts.js +47 -13
- package/dist/session/provider-cli/provider-cli.deterministic.js +34 -2
- package/dist/session/provider-cli/provider-cli.process.d.ts +51 -0
- package/dist/session/provider-cli/provider-cli.process.js +422 -22
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +5 -0
- package/dist/session/provider-cli/provider-cli.prompts.js +136 -16
- package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
- package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
- package/dist/session/provider-cli/provider-cli.runtime.d.ts +14 -1
- package/dist/session/provider-cli/provider-cli.runtime.js +54 -3
- package/dist/session/provider-cli/provider-cli.service.d.ts +30 -5
- package/dist/session/provider-cli/provider-cli.service.js +773 -69
- package/dist/session/provider-cli/provider-cli.types.d.ts +2 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +10 -0
- package/dist/session/provider-cli/provider-output-state.js +361 -17
- package/dist/session/provider-cli/provider-usage.d.ts +7 -0
- package/dist/session/provider-cli/provider-usage.js +28 -5
- package/dist/session/session-redaction.js +434 -9
- package/dist/session/session.service.js +145 -23
- package/dist/session/session.types.d.ts +147 -2
- package/dist/session/session.types.js +587 -7
- package/dist/session/unreferenced-exports.d.ts +12 -0
- package/dist/session/unreferenced-exports.js +59 -0
- package/dist/shared/format/format-bytes.d.ts +1 -0
- package/dist/shared/format/format-bytes.js +9 -0
- package/dist/shared/git/git.js +2 -1
- package/dist/shared/path/path.d.ts +1 -0
- package/dist/shared/path/path.js +18 -0
- package/dist/shared/process/dotnet-toolchain.d.ts +17 -0
- package/dist/shared/process/dotnet-toolchain.js +307 -0
- package/dist/shared/process/host-process-supervisor.d.ts +2 -0
- package/dist/shared/process/host-process-supervisor.js +57 -9
- package/dist/shared/process/process.d.ts +2 -0
- package/dist/shared/process/process.js +12 -7
- package/dist/shared/provider-capabilities.d.ts +3 -0
- package/dist/shared/provider-capabilities.js +85 -0
- package/dist/shared/redaction/redaction.d.ts +1 -0
- package/dist/shared/redaction/redaction.js +39 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/dependency-warmer.d.ts +41 -9
- package/dist/worktree/dependency-warmer.js +497 -22
- package/dist/worktree/index.d.ts +1 -1
- package/dist/worktree/story-qa-reference-hydrator.d.ts +4 -0
- package/dist/worktree/story-qa-reference-hydrator.js +253 -0
- package/dist/worktree/worktree.service.d.ts +7 -0
- package/dist/worktree/worktree.service.js +253 -21
- package/dist/worktree/worktree.types.d.ts +7 -1
- package/package.json +7 -5
- package/runtime/windows-job-supervisor.ps1 +380 -14
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { redactText } from "../shared/redaction/redaction.js";
|
|
3
|
+
export class ForgeFailureError extends Error {
|
|
4
|
+
failureCode;
|
|
5
|
+
failureSource;
|
|
6
|
+
constructor(failureCode, message, failureSource = "bridge", options) {
|
|
7
|
+
super(message, options);
|
|
8
|
+
this.name = "ForgeFailureError";
|
|
9
|
+
this.failureCode = normalizeFailureCode(failureCode);
|
|
10
|
+
this.failureSource = failureSource;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
2
13
|
export function parseRunOnceDispatchPacket(value) {
|
|
3
14
|
const packet = asObject(value, "dispatch packet");
|
|
4
15
|
const schemaVersion = requiredNumber(packet, "schemaVersion");
|
|
@@ -34,6 +45,134 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
34
45
|
draft: parseDraftPrompt(packet.draft)
|
|
35
46
|
};
|
|
36
47
|
}
|
|
48
|
+
export function parseRunOnceResultPacket(value) {
|
|
49
|
+
const packet = asObject(value, "run-once result packet");
|
|
50
|
+
const schemaVersion = requiredNumber(packet, "schemaVersion");
|
|
51
|
+
if (schemaVersion !== 1 && schemaVersion !== 2) {
|
|
52
|
+
throw new Error(`Unsupported run-once result schemaVersion: ${schemaVersion}`);
|
|
53
|
+
}
|
|
54
|
+
const exitCode = requiredNumber(packet, "exitCode");
|
|
55
|
+
if (!Number.isSafeInteger(exitCode))
|
|
56
|
+
throw new Error("exitCode must be an integer.");
|
|
57
|
+
requiredString(packet, "summary");
|
|
58
|
+
if (!Array.isArray(packet.events))
|
|
59
|
+
throw new Error("events must be an array.");
|
|
60
|
+
for (const item of packet.events) {
|
|
61
|
+
if (typeof item !== "object" || item === null || !("wait" in item))
|
|
62
|
+
continue;
|
|
63
|
+
const event = asObject(item, "run-once result event");
|
|
64
|
+
if (event.wait !== undefined && event.wait !== null) {
|
|
65
|
+
parseWaitEnvelope(event.wait, requiredString(event, "eventType"));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (schemaVersion === 1) {
|
|
69
|
+
if (packet.failure !== undefined && packet.failure !== null) {
|
|
70
|
+
throw new Error("run-once result schemaVersion 1 cannot contain a v2 failure envelope.");
|
|
71
|
+
}
|
|
72
|
+
return packet;
|
|
73
|
+
}
|
|
74
|
+
if (exitCode === 0 && packet.failure !== undefined && packet.failure !== null) {
|
|
75
|
+
throw new Error("run-once result cannot report both success and a failure envelope.");
|
|
76
|
+
}
|
|
77
|
+
if (exitCode !== 0 && (packet.failure === undefined || packet.failure === null)) {
|
|
78
|
+
throw new Error("run-once result schemaVersion 2 requires a failure envelope for a nonzero exit.");
|
|
79
|
+
}
|
|
80
|
+
if (packet.failure !== undefined && packet.failure !== null)
|
|
81
|
+
parseFailureEnvelope(packet.failure);
|
|
82
|
+
return packet;
|
|
83
|
+
}
|
|
84
|
+
const waitKinds = new Map([
|
|
85
|
+
["host_runtime_admission_waiting", "host_runtime_admission"],
|
|
86
|
+
["host_runtime_admission_acquired", "host_runtime_admission"],
|
|
87
|
+
["host_runtime_serial_admission_waiting", "host_runtime_serial_admission"],
|
|
88
|
+
["host_runtime_prepared_probe_started", "host_runtime_prepared_probe"],
|
|
89
|
+
["host_runtime_prepared_probe_progress", "host_runtime_prepared_probe"],
|
|
90
|
+
["host_runtime_prepared_probe_completed", "host_runtime_prepared_probe"],
|
|
91
|
+
["host_runtime_prepared_admission_waiting", "host_runtime_prepared_admission"],
|
|
92
|
+
["host_runtime_prepared_admission_acquired", "host_runtime_prepared_admission"],
|
|
93
|
+
]);
|
|
94
|
+
export function waitKindForEvent(eventType) {
|
|
95
|
+
const kind = waitKinds.get(eventType);
|
|
96
|
+
if (!kind)
|
|
97
|
+
throw new Error(`Unsupported typed wait observation '${eventType}'.`);
|
|
98
|
+
return kind;
|
|
99
|
+
}
|
|
100
|
+
export function waitWakeCondition(kind) {
|
|
101
|
+
switch (kind) {
|
|
102
|
+
case "host_runtime_serial_admission":
|
|
103
|
+
return "project_singleton_released";
|
|
104
|
+
case "host_runtime_prepared_probe":
|
|
105
|
+
return "prepared_runtime_probe_completed";
|
|
106
|
+
default:
|
|
107
|
+
return "host_runtime_admission_available";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function parseWaitEnvelope(value, eventType) {
|
|
111
|
+
const wait = asObject(value, "wait envelope");
|
|
112
|
+
if (requiredNumber(wait, "schemaVersion") !== 2) {
|
|
113
|
+
throw new Error("Unsupported wait envelope schemaVersion.");
|
|
114
|
+
}
|
|
115
|
+
let expectedKind;
|
|
116
|
+
try {
|
|
117
|
+
expectedKind = waitKindForEvent(eventType);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
throw new Error("wait envelope is only valid on a wait lifecycle observation.");
|
|
121
|
+
}
|
|
122
|
+
const kind = requiredString(wait, "kind");
|
|
123
|
+
const scope = requiredString(wait, "scope");
|
|
124
|
+
const wakeCondition = requiredString(wait, "wakeCondition");
|
|
125
|
+
if (kind !== expectedKind
|
|
126
|
+
|| !isWaitCode(kind, 80)
|
|
127
|
+
|| !isWaitCode(scope, 64)
|
|
128
|
+
|| !isWaitCode(wakeCondition, 80)
|
|
129
|
+
|| wakeCondition !== waitWakeCondition(kind)) {
|
|
130
|
+
throw new Error("wait envelope kind, scope, or wakeCondition is invalid.");
|
|
131
|
+
}
|
|
132
|
+
if (!Array.isArray(wait.resourceKeys)
|
|
133
|
+
|| wait.resourceKeys.length > 16
|
|
134
|
+
|| wait.resourceKeys.some(item => typeof item !== "string" || !isWaitCode(item, 256))
|
|
135
|
+
|| new Set(wait.resourceKeys).size !== wait.resourceKeys.length) {
|
|
136
|
+
throw new Error("wait envelope resourceKeys are invalid, duplicated, or exceed their bound.");
|
|
137
|
+
}
|
|
138
|
+
const queuePosition = wait.queuePosition;
|
|
139
|
+
if (queuePosition !== undefined && queuePosition !== null
|
|
140
|
+
&& (typeof queuePosition !== "number" || !Number.isSafeInteger(queuePosition) || queuePosition <= 0)) {
|
|
141
|
+
throw new Error("wait envelope queuePosition is invalid.");
|
|
142
|
+
}
|
|
143
|
+
const blockerSessionId = wait.blockerSessionId;
|
|
144
|
+
if (blockerSessionId !== undefined && blockerSessionId !== null
|
|
145
|
+
&& (typeof blockerSessionId !== "string"
|
|
146
|
+
|| !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(blockerSessionId)
|
|
147
|
+
|| /^0{8}-0{4}-0{4}-0{4}-0{12}$/u.test(blockerSessionId))) {
|
|
148
|
+
throw new Error("wait envelope blockerSessionId is invalid.");
|
|
149
|
+
}
|
|
150
|
+
const blockerTitle = wait.blockerTitle;
|
|
151
|
+
if (blockerTitle !== undefined && blockerTitle !== null
|
|
152
|
+
&& (typeof blockerTitle !== "string" || blockerTitle.length > 500)) {
|
|
153
|
+
throw new Error("wait envelope blockerTitle is invalid.");
|
|
154
|
+
}
|
|
155
|
+
const retryAt = wait.retryAt;
|
|
156
|
+
if (retryAt !== undefined && retryAt !== null
|
|
157
|
+
&& (typeof retryAt !== "string" || !Number.isFinite(Date.parse(retryAt)))) {
|
|
158
|
+
throw new Error("wait envelope retryAt is invalid.");
|
|
159
|
+
}
|
|
160
|
+
const firstObservedAt = requiredString(wait, "firstObservedAt");
|
|
161
|
+
if (!Number.isFinite(Date.parse(firstObservedAt))) {
|
|
162
|
+
throw new Error("wait envelope firstObservedAt is invalid.");
|
|
163
|
+
}
|
|
164
|
+
const observationVersion = requiredNumber(wait, "observationVersion");
|
|
165
|
+
if (!Number.isSafeInteger(observationVersion) || observationVersion <= 0) {
|
|
166
|
+
throw new Error("wait envelope observationVersion is invalid.");
|
|
167
|
+
}
|
|
168
|
+
if (requiredString(wait, "contractOrigin") !== "bridge_v2") {
|
|
169
|
+
throw new Error("wait envelope contractOrigin must identify the v2 Bridge writer.");
|
|
170
|
+
}
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
function isWaitCode(value, maximumLength) {
|
|
174
|
+
return value.length <= maximumLength && /^[a-z][a-z0-9_.:-]*$/u.test(value);
|
|
175
|
+
}
|
|
37
176
|
function parseRuntimeIntent(value) {
|
|
38
177
|
if (value === undefined || value === null) {
|
|
39
178
|
return { executionClass: "managed_runtime", applicationRuntimeRequired: true };
|
|
@@ -171,17 +310,391 @@ function parseSandboxNetworkPolicy(value) {
|
|
|
171
310
|
}
|
|
172
311
|
return { mode, allowedHosts };
|
|
173
312
|
}
|
|
174
|
-
|
|
313
|
+
const failureCodePattern = /^[a-z][a-z0-9_.-]{0,79}$/u;
|
|
314
|
+
const failureCategories = new Set(["domain", "provider", "transport", "cancellation", "ownership", "resource", "unknown"]);
|
|
315
|
+
const failurePhases = new Set(["offer", "build", "prepare", "agent", "validation", "import", "teardown", "unknown"]);
|
|
316
|
+
const failureSources = new Set(["bridge", "provider", "sandbox", "unknown"]);
|
|
317
|
+
const failureRetryabilities = new Set(["retryable", "non_retryable", "policy_required", "unknown"]);
|
|
318
|
+
const failureOperatorActions = new Set(["none", "retry_dispatch", "retry_with_policy", "inspect_failure", "fix_input", "fix_environment", "reauthenticate", "restore_ownership"]);
|
|
319
|
+
const maximumFailureMessageLength = 2000;
|
|
320
|
+
const maximumFailureCausalChainLength = 8;
|
|
321
|
+
const waitConditionCodes = new Set([
|
|
322
|
+
"host_runtime_admission_waiting",
|
|
323
|
+
"host_runtime_serial_admission_waiting",
|
|
324
|
+
"host_runtime_prepared_probe_started",
|
|
325
|
+
"host_runtime_prepared_probe_progress",
|
|
326
|
+
"host_runtime_prepared_admission_waiting",
|
|
327
|
+
"waiting_for_capacity",
|
|
328
|
+
]);
|
|
329
|
+
const stableFailureFamilyCodes = new Set([
|
|
330
|
+
"generated_output_reset_invalid",
|
|
331
|
+
"generated_output_reset_unsafe",
|
|
332
|
+
"sandbox_build_failed",
|
|
333
|
+
"sandbox_build_policy_failed",
|
|
334
|
+
"sandbox_builder_release_failed",
|
|
335
|
+
"sandbox_config_commit_invalid",
|
|
336
|
+
"sandbox_config_commit_missing",
|
|
337
|
+
"sandbox_config_commit_unapproved",
|
|
338
|
+
"contract_invalid",
|
|
339
|
+
"sandbox_input_failed",
|
|
340
|
+
"docker_capacity_unavailable",
|
|
341
|
+
"docker_engine_invalid_response",
|
|
342
|
+
"docker_engine_recovery_failed",
|
|
343
|
+
"docker_engine_recovery_timeout",
|
|
344
|
+
"docker_linux_required",
|
|
345
|
+
"docker_preflight_required",
|
|
346
|
+
"docker_rootless_required",
|
|
347
|
+
"docker_unavailable",
|
|
348
|
+
"docker_unreachable",
|
|
349
|
+
"sandbox_output_conflict",
|
|
350
|
+
"sandbox_output_invalid",
|
|
351
|
+
"sandbox_output_missing",
|
|
352
|
+
"sandbox_output_publish_forbidden",
|
|
353
|
+
"sandbox_output_rejected",
|
|
354
|
+
"sandbox_output_publish_rejected",
|
|
355
|
+
"sandbox_output_publish_stale",
|
|
356
|
+
"sandbox_output_publish_unavailable",
|
|
357
|
+
"sandbox_output_stale",
|
|
358
|
+
"sandbox_import_cancelled",
|
|
359
|
+
"sandbox_import_unavailable",
|
|
360
|
+
"teardown_failed",
|
|
361
|
+
"reaper_cleanup_degraded",
|
|
362
|
+
"reaper_runner_replaced",
|
|
363
|
+
"sandbox_egress_policy_invalid",
|
|
364
|
+
"sandbox_egress_proxy_unavailable",
|
|
365
|
+
"sandbox_event_flood",
|
|
366
|
+
"sandbox_event_invalid",
|
|
367
|
+
"sandbox_identity_invalid",
|
|
368
|
+
"sandbox_image_signature_failed",
|
|
369
|
+
"sandbox_log_limit",
|
|
370
|
+
"sandbox_preflight_failed",
|
|
371
|
+
"sandbox_security_profile_invalid",
|
|
372
|
+
"sandbox_security_profile_unavailable",
|
|
373
|
+
"sandbox_service_health_policy_invalid",
|
|
374
|
+
]);
|
|
375
|
+
export function failureResult(summary, events = [], facts = failureFactsForCode("bridge.execution_failed")) {
|
|
175
376
|
return {
|
|
176
|
-
schemaVersion:
|
|
377
|
+
schemaVersion: 2,
|
|
177
378
|
exitCode: 1,
|
|
178
|
-
summary,
|
|
179
|
-
events
|
|
379
|
+
summary: boundedFailureText(summary),
|
|
380
|
+
events,
|
|
381
|
+
failure: createFailureEnvelope(summary, facts),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
export function createFailureEnvelope(message, facts, occurredAt = new Date().toISOString()) {
|
|
385
|
+
const code = normalizeFailureCode(facts.code);
|
|
386
|
+
if (waitConditionCodes.has(code)) {
|
|
387
|
+
throw new Error(`Wait condition '${code}' must be emitted as lifecycle state, not a terminal failure.`);
|
|
388
|
+
}
|
|
389
|
+
assertFailureFactsMatchPolicy(code, facts);
|
|
390
|
+
return {
|
|
391
|
+
schemaVersion: 2,
|
|
392
|
+
code,
|
|
393
|
+
message: boundedFailureText(message),
|
|
394
|
+
category: facts.category,
|
|
395
|
+
phase: facts.phase,
|
|
396
|
+
source: facts.source,
|
|
397
|
+
retryability: facts.retryability,
|
|
398
|
+
operatorAction: facts.operatorAction,
|
|
399
|
+
causalChain: (facts.causalChain ?? []).slice(0, maximumFailureCausalChainLength).map(cause => ({
|
|
400
|
+
code: normalizeFailureCode(cause.code),
|
|
401
|
+
message: boundedFailureText(cause.message),
|
|
402
|
+
source: cause.source,
|
|
403
|
+
})),
|
|
404
|
+
occurredAt,
|
|
405
|
+
contractOrigin: "bridge_v2",
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
export function failureFactsFromError(error, options = {}) {
|
|
409
|
+
if (options.cancelled) {
|
|
410
|
+
const cancellationSource = options.source ?? "bridge";
|
|
411
|
+
return {
|
|
412
|
+
...failureFactsForCode("bridge.dispatch_cancelled", cancellationSource),
|
|
413
|
+
causalChain: errorCauseChain(error, cancellationSource),
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const source = error instanceof ForgeFailureError
|
|
417
|
+
? error.failureSource
|
|
418
|
+
: options.source ?? "bridge";
|
|
419
|
+
const code = error instanceof ForgeFailureError
|
|
420
|
+
? error.failureCode
|
|
421
|
+
: "bridge.execution_failed";
|
|
422
|
+
return {
|
|
423
|
+
...failureFactsForCode(code, source),
|
|
424
|
+
causalChain: errorCauseChain(error, source),
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
export function failureFactsForCode(code, source = "bridge") {
|
|
428
|
+
const policy = failurePolicyForCode(code);
|
|
429
|
+
if (policy.known && !policy.allowedSources.includes(source)) {
|
|
430
|
+
throw new Error(`Failure code '${policy.code}' cannot be attributed to source '${source}'.`);
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
code: policy.code,
|
|
434
|
+
category: policy.category,
|
|
435
|
+
phase: policy.phase,
|
|
436
|
+
source,
|
|
437
|
+
retryability: policy.retryability,
|
|
438
|
+
operatorAction: policy.operatorAction,
|
|
180
439
|
};
|
|
181
440
|
}
|
|
441
|
+
export function failurePolicyForCode(code) {
|
|
442
|
+
code = normalizeFailureCode(code);
|
|
443
|
+
const classification = unvalidatedFailureFactsForCode(code, "bridge");
|
|
444
|
+
const known = classification.category !== "unknown" || code === "legacy_failure_unknown";
|
|
445
|
+
return {
|
|
446
|
+
code,
|
|
447
|
+
category: classification.category,
|
|
448
|
+
phase: classification.phase,
|
|
449
|
+
retryability: classification.retryability,
|
|
450
|
+
operatorAction: classification.operatorAction,
|
|
451
|
+
allowedSources: allowedSourcesForCode(code, known),
|
|
452
|
+
known,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function unvalidatedFailureFactsForCode(code, source) {
|
|
456
|
+
code = normalizeFailureCode(code);
|
|
457
|
+
if (["bridge.dispatch_cancelled", "dispatch_cancelled", "dispatch_session_terminal", "dispatch.capacity_retargeted"].includes(code)) {
|
|
458
|
+
return facts(code, "cancellation", "teardown", source, "non_retryable", "none");
|
|
459
|
+
}
|
|
460
|
+
if (["bridge.dispatch_fence_conflict", "bridge.reservation_stale", "bridge.capacity_wait_lost", "bridge.dispatch_attempt_missing"].includes(code)) {
|
|
461
|
+
return facts(code, "ownership", "offer", source, "non_retryable", "restore_ownership");
|
|
462
|
+
}
|
|
463
|
+
if (["reservation_expired", "bridge.lease_expired", "bridge.stale_runner_instance", "bridge.runner_replaced"].includes(code)) {
|
|
464
|
+
return facts(code, "ownership", "offer", source, "retryable", "retry_dispatch");
|
|
465
|
+
}
|
|
466
|
+
if (["bridge.dispatch_failed", "bridge.execution_failed", "bridge.cli_execution_failed", "bridge_callback_timeout"].includes(code)) {
|
|
467
|
+
return facts(code, "transport", "unknown", source, "policy_required", "inspect_failure");
|
|
468
|
+
}
|
|
469
|
+
if (code === "bridge.unavailable") {
|
|
470
|
+
return facts(code, "transport", "offer", source, "retryable", "retry_dispatch");
|
|
471
|
+
}
|
|
472
|
+
if (["capacity_unavailable", "bridge.runner_draining", "docker_engine_unavailable", "docker_memory_low", "docker_memory_sample_unavailable", "docker_cpu_low", "docker_disk_low", "server_local.resource_pressure", "server_local.capacity_unavailable", "dispatch.preparation_resource_pressure", "sandbox_capacity_profile_unavailable", "host_runtime_admission_expired"].includes(code)) {
|
|
473
|
+
return facts(code, "resource", "offer", source, "retryable", "retry_dispatch");
|
|
474
|
+
}
|
|
475
|
+
if (["bridge.lease_retry_exhausted", "bridge.capacity_wait_exhausted"].includes(code)) {
|
|
476
|
+
return facts(code, "resource", "offer", source, "non_retryable", "inspect_failure");
|
|
477
|
+
}
|
|
478
|
+
if (["prepublication_infrastructure_failed", "prepublication_capability_unavailable"].includes(code)) {
|
|
479
|
+
return facts(code, "resource", "validation", source, "retryable", "retry_with_policy");
|
|
480
|
+
}
|
|
481
|
+
if (["prepublication_validation_failed", "prepublication_contract_invalid", "prepublication_execution_scope_invalid"].includes(code)) {
|
|
482
|
+
return facts(code, "domain", "validation", source, "non_retryable", "fix_input");
|
|
483
|
+
}
|
|
484
|
+
if (["generated_output_reset_failed", "generated_output_reset_unverified", "worktree_prepare_failed", "host_runtime_ledger_write_failed", "host_diagnostics_temporary_unavailable", "attempt_process_supervisor_start_failed", "attempt_process_supervisor_start_timeout"].includes(code)) {
|
|
485
|
+
return facts(code, "transport", "prepare", source, "retryable", "retry_dispatch");
|
|
486
|
+
}
|
|
487
|
+
if (stableFailureFamilyCodes.has(code) && code.startsWith("generated_output_reset_")) {
|
|
488
|
+
return facts(code, "domain", "prepare", source, "non_retryable", "fix_environment");
|
|
489
|
+
}
|
|
490
|
+
if (code === "provider_start_unavailable") {
|
|
491
|
+
return facts(code, "provider", "prepare", source, "retryable", "retry_dispatch");
|
|
492
|
+
}
|
|
493
|
+
if (code === "provider_configuration_invalid") {
|
|
494
|
+
return facts(code, "provider", "prepare", source, "non_retryable", "fix_environment");
|
|
495
|
+
}
|
|
496
|
+
if (["host_port_collision", "host_runtime_process_exited", "host_runtime_listener_timeout", "host_runtime_http_readiness_timeout", "host_runtime_prepared_source_changed", "host_runtime_prepared_probe_incomplete", "host_runtime_prepared_probe_cleanup_unconfirmed"].includes(code)) {
|
|
497
|
+
return facts(code, "resource", "prepare", source, "retryable", "retry_dispatch");
|
|
498
|
+
}
|
|
499
|
+
if (code === "host_runtime_project_start_failed") {
|
|
500
|
+
return facts(code, "domain", "prepare", source, "non_retryable", "fix_environment");
|
|
501
|
+
}
|
|
502
|
+
if (stableFailureFamilyCodes.has(code) && (code.startsWith("sandbox_build") || code.startsWith("image_"))) {
|
|
503
|
+
const policyFailure = code === "sandbox_build_policy_failed";
|
|
504
|
+
return facts(code, "resource", "build", source, policyFailure ? "non_retryable" : "retryable", policyFailure ? "fix_environment" : "retry_dispatch");
|
|
505
|
+
}
|
|
506
|
+
if (code === "provider_network_policy_denied"
|
|
507
|
+
|| stableFailureFamilyCodes.has(code) && (code.startsWith("sandbox_config") || code.startsWith("contract_") || code.startsWith("sandbox_input"))) {
|
|
508
|
+
return facts(code, "domain", "prepare", source, "non_retryable", "fix_environment");
|
|
509
|
+
}
|
|
510
|
+
if (stableFailureFamilyCodes.has(code) && code.startsWith("docker_")) {
|
|
511
|
+
return facts(code, "resource", "prepare", source, "non_retryable", "fix_environment");
|
|
512
|
+
}
|
|
513
|
+
if (code === "provider_network_unavailable") {
|
|
514
|
+
return facts(code, "transport", "offer", source, "retryable", "retry_dispatch");
|
|
515
|
+
}
|
|
516
|
+
if (code === "provider_request_not_found") {
|
|
517
|
+
return facts(code, "provider", "offer", source, "retryable", "retry_dispatch");
|
|
518
|
+
}
|
|
519
|
+
if (code === "provider_capacity_unavailable") {
|
|
520
|
+
return facts(code, "resource", "offer", source, "retryable", "retry_dispatch");
|
|
521
|
+
}
|
|
522
|
+
if (code === "provider_context_window_exhausted") {
|
|
523
|
+
return facts(code, "provider", "agent", source, "retryable", "retry_dispatch");
|
|
524
|
+
}
|
|
525
|
+
if (stableFailureFamilyCodes.has(code) && (code.startsWith("sandbox_output") || code.startsWith("sandbox_import"))) {
|
|
526
|
+
const retryable = code === "sandbox_import_unavailable" || code === "sandbox_output_publish_unavailable";
|
|
527
|
+
return facts(code, "transport", "import", source, retryable ? "retryable" : "non_retryable", retryable ? "retry_with_policy" : "inspect_failure");
|
|
528
|
+
}
|
|
529
|
+
if (stableFailureFamilyCodes.has(code) && (code.startsWith("teardown") || code.startsWith("reaper"))) {
|
|
530
|
+
return facts(code, "transport", "teardown", source, "retryable", "retry_with_policy");
|
|
531
|
+
}
|
|
532
|
+
if (["invalid_dispatch"].includes(code)) {
|
|
533
|
+
return facts(code, "domain", "offer", source, "non_retryable", "fix_input");
|
|
534
|
+
}
|
|
535
|
+
if ([
|
|
536
|
+
"bridge.api_key_auth_required",
|
|
537
|
+
"bridge.api_key_secret_grant_missing",
|
|
538
|
+
"bridge.api_key_secret_grant_denied",
|
|
539
|
+
"bridge.api_key_secret_grant_provider_mismatch",
|
|
540
|
+
"bridge.api_key_secret_missing",
|
|
541
|
+
"contract_plaintext_secret_rejected",
|
|
542
|
+
].includes(code)) {
|
|
543
|
+
return facts(code, "domain", "prepare", source, "non_retryable", "reauthenticate");
|
|
544
|
+
}
|
|
545
|
+
if (code === "sandbox_idle_timeout") {
|
|
546
|
+
return facts(code, "provider", "agent", source, "retryable", "retry_with_policy");
|
|
547
|
+
}
|
|
548
|
+
if (code === "sandbox_wall_timeout") {
|
|
549
|
+
return facts(code, "provider", "agent", source, "retryable", "retry_with_policy");
|
|
550
|
+
}
|
|
551
|
+
if (["sandbox_oom_killed", "sandbox_pid_exhausted", "sandbox_file_descriptor_exhausted", "sandbox_storage_exhausted"].includes(code)) {
|
|
552
|
+
return facts(code, "resource", "agent", source, "non_retryable", "inspect_failure");
|
|
553
|
+
}
|
|
554
|
+
if (code === "sandbox_auth_unavailable") {
|
|
555
|
+
return facts(code, "domain", "prepare", source, "non_retryable", "reauthenticate");
|
|
556
|
+
}
|
|
557
|
+
if (code === "sandbox_lease_expired") {
|
|
558
|
+
return facts(code, "ownership", "agent", source, "non_retryable", "restore_ownership");
|
|
559
|
+
}
|
|
560
|
+
if (code === "sandbox_runtime_failed") {
|
|
561
|
+
return facts(code, "provider", "agent", source, "non_retryable", "inspect_failure");
|
|
562
|
+
}
|
|
563
|
+
if (code === "worktree_branch_integration_conflict") {
|
|
564
|
+
return facts(code, "domain", "agent", source, "non_retryable", "fix_input");
|
|
565
|
+
}
|
|
566
|
+
if (stableFailureFamilyCodes.has(code) && code.startsWith("sandbox_")) {
|
|
567
|
+
return facts(code, "provider", "agent", source, "non_retryable", "inspect_failure");
|
|
568
|
+
}
|
|
569
|
+
return facts(code, "unknown", "unknown", source, "unknown", "inspect_failure");
|
|
570
|
+
}
|
|
571
|
+
function parseFailureEnvelope(value) {
|
|
572
|
+
const failure = asObject(value, "failure");
|
|
573
|
+
if (requiredNumber(failure, "schemaVersion") !== 2)
|
|
574
|
+
throw new Error("failure.schemaVersion must be 2.");
|
|
575
|
+
const code = requiredString(failure, "code");
|
|
576
|
+
const message = requiredString(failure, "message");
|
|
577
|
+
const category = requiredString(failure, "category");
|
|
578
|
+
const phase = requiredString(failure, "phase");
|
|
579
|
+
const source = requiredString(failure, "source");
|
|
580
|
+
const retryability = requiredString(failure, "retryability");
|
|
581
|
+
const operatorAction = requiredString(failure, "operatorAction");
|
|
582
|
+
const occurredAt = requiredString(failure, "occurredAt");
|
|
583
|
+
const contractOrigin = requiredString(failure, "contractOrigin");
|
|
584
|
+
if (!failureCodePattern.test(code))
|
|
585
|
+
throw new Error("failure.code is invalid.");
|
|
586
|
+
if (message.length > maximumFailureMessageLength)
|
|
587
|
+
throw new Error("failure.message is too long.");
|
|
588
|
+
if (!failureCategories.has(category) || !failurePhases.has(phase) || !failureSources.has(source)
|
|
589
|
+
|| !failureRetryabilities.has(retryability) || !failureOperatorActions.has(operatorAction)) {
|
|
590
|
+
throw new Error("failure contains an unsupported category, phase, source, retryability, or operator action.");
|
|
591
|
+
}
|
|
592
|
+
assertFailureFactsMatchPolicy(code, { code, category, phase, source, retryability, operatorAction });
|
|
593
|
+
if (!Number.isFinite(Date.parse(occurredAt)))
|
|
594
|
+
throw new Error("failure.occurredAt is invalid.");
|
|
595
|
+
if (contractOrigin !== "bridge_v2")
|
|
596
|
+
throw new Error("failure.contractOrigin must be bridge_v2.");
|
|
597
|
+
if (!Array.isArray(failure.causalChain) || failure.causalChain.length > maximumFailureCausalChainLength) {
|
|
598
|
+
throw new Error("failure.causalChain is invalid or too long.");
|
|
599
|
+
}
|
|
600
|
+
const causalChain = failure.causalChain.map((candidate, index) => {
|
|
601
|
+
const cause = asObject(candidate, `failure.causalChain[${index}]`);
|
|
602
|
+
const causeCode = requiredString(cause, "code");
|
|
603
|
+
const causeMessage = requiredString(cause, "message");
|
|
604
|
+
const causeSource = requiredString(cause, "source");
|
|
605
|
+
if (!failureCodePattern.test(causeCode) || causeMessage.length > maximumFailureMessageLength || !failureSources.has(causeSource)) {
|
|
606
|
+
throw new Error(`failure.causalChain[${index}] is invalid.`);
|
|
607
|
+
}
|
|
608
|
+
return { code: causeCode, message: causeMessage, source: causeSource };
|
|
609
|
+
});
|
|
610
|
+
return { schemaVersion: 2, code, message, category, phase, source, retryability, operatorAction, causalChain, occurredAt, contractOrigin };
|
|
611
|
+
}
|
|
612
|
+
function facts(code, category, phase, source, retryability, operatorAction) {
|
|
613
|
+
return { code, category, phase, source, retryability, operatorAction };
|
|
614
|
+
}
|
|
615
|
+
function allowedSourcesForCode(code, known) {
|
|
616
|
+
if (!known)
|
|
617
|
+
return ["bridge", "provider", "sandbox", "unknown"];
|
|
618
|
+
if (code.startsWith("provider_"))
|
|
619
|
+
return ["bridge", "provider", "sandbox"];
|
|
620
|
+
if (code.startsWith("sandbox_")
|
|
621
|
+
|| code.startsWith("docker_")
|
|
622
|
+
|| code.startsWith("image_")
|
|
623
|
+
|| code.startsWith("contract_")
|
|
624
|
+
|| code.startsWith("generated_output_reset_")
|
|
625
|
+
|| code.startsWith("teardown")
|
|
626
|
+
|| code.startsWith("reaper")) {
|
|
627
|
+
return ["bridge", "sandbox"];
|
|
628
|
+
}
|
|
629
|
+
return ["bridge"];
|
|
630
|
+
}
|
|
631
|
+
function assertFailureFactsMatchPolicy(code, factsToValidate) {
|
|
632
|
+
const policy = failurePolicyForCode(code);
|
|
633
|
+
if (!policy.known)
|
|
634
|
+
return;
|
|
635
|
+
if (factsToValidate.category !== policy.category
|
|
636
|
+
|| factsToValidate.phase !== policy.phase
|
|
637
|
+
|| factsToValidate.retryability !== policy.retryability
|
|
638
|
+
|| factsToValidate.operatorAction !== policy.operatorAction
|
|
639
|
+
|| !policy.allowedSources.includes(factsToValidate.source)) {
|
|
640
|
+
throw new Error(`Failure code '${policy.code}' contradicts the failure taxonomy or allowed-source registry.`);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function normalizeFailureCode(value) {
|
|
644
|
+
const normalized = value.trim().toLowerCase().slice(0, 80);
|
|
645
|
+
return failureCodePattern.test(normalized) ? normalized : "bridge.execution_failed";
|
|
646
|
+
}
|
|
647
|
+
function boundedFailureText(value) {
|
|
648
|
+
const bounded = value.trim().slice(0, maximumFailureMessageLength);
|
|
649
|
+
return bounded || "Dispatch failed without a diagnostic message.";
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Rebuilds an error graph at a privileged execution boundary without
|
|
653
|
+
* retaining the raw error or any raw recursive cause. Forge failure metadata
|
|
654
|
+
* survives so the P7 taxonomy remains authoritative after sanitization.
|
|
655
|
+
*/
|
|
656
|
+
export function sanitizeFailureError(error, exactSecrets = []) {
|
|
657
|
+
return sanitizeFailureErrorAtDepth(error, exactSecrets, new Set(), 0);
|
|
658
|
+
}
|
|
659
|
+
function sanitizeFailureErrorAtDepth(error, exactSecrets, visited, depth) {
|
|
660
|
+
if (depth >= maximumFailureCausalChainLength) {
|
|
661
|
+
return new Error("Additional error causes were omitted.");
|
|
662
|
+
}
|
|
663
|
+
if (error !== null && typeof error === "object") {
|
|
664
|
+
if (visited.has(error))
|
|
665
|
+
return new Error("Circular error cause was omitted.");
|
|
666
|
+
visited.add(error);
|
|
667
|
+
}
|
|
668
|
+
const message = redactText(error instanceof Error ? error.message : String(error), exactSecrets);
|
|
669
|
+
const cause = error instanceof Error && error.cause !== undefined
|
|
670
|
+
? sanitizeFailureErrorAtDepth(error.cause, exactSecrets, visited, depth + 1)
|
|
671
|
+
: undefined;
|
|
672
|
+
const options = cause === undefined ? undefined : { cause };
|
|
673
|
+
if (error instanceof ForgeFailureError) {
|
|
674
|
+
return new ForgeFailureError(error.failureCode, message, error.failureSource, options);
|
|
675
|
+
}
|
|
676
|
+
if (error instanceof AggregateError) {
|
|
677
|
+
const errors = [...error.errors].map(item => sanitizeFailureErrorAtDepth(item, exactSecrets, visited, depth + 1));
|
|
678
|
+
return new AggregateError(errors, message, options);
|
|
679
|
+
}
|
|
680
|
+
return new Error(message, options);
|
|
681
|
+
}
|
|
682
|
+
function errorCauseChain(error, source) {
|
|
683
|
+
const causes = [];
|
|
684
|
+
let current = error;
|
|
685
|
+
while (current !== undefined && current !== null && causes.length < maximumFailureCausalChainLength) {
|
|
686
|
+
const message = current instanceof Error ? current.message : String(current);
|
|
687
|
+
const code = current instanceof ForgeFailureError ? current.failureCode : "cause_unknown";
|
|
688
|
+
const causeSource = current instanceof ForgeFailureError ? current.failureSource : source;
|
|
689
|
+
causes.push({ code, message: boundedFailureText(message), source: causeSource });
|
|
690
|
+
current = current instanceof Error ? current.cause : undefined;
|
|
691
|
+
}
|
|
692
|
+
return causes;
|
|
693
|
+
}
|
|
182
694
|
function parseWorkItem(value) {
|
|
183
695
|
const item = asObject(value, "workItem");
|
|
184
696
|
return {
|
|
697
|
+
projectId: optionalString(item, "projectId"),
|
|
185
698
|
id: requiredString(item, "id"),
|
|
186
699
|
kind: requiredString(item, "kind"),
|
|
187
700
|
title: requiredString(item, "title"),
|
|
@@ -205,6 +718,7 @@ function parseRepo(value) {
|
|
|
205
718
|
function parseCycleRun(value) {
|
|
206
719
|
const cycle = asObject(value, "cycleRun");
|
|
207
720
|
return {
|
|
721
|
+
effectivePlanAmendmentHash: optionalString(cycle, "effectivePlanAmendmentHash"),
|
|
208
722
|
id: requiredString(cycle, "id"),
|
|
209
723
|
ownerType: optionalString(cycle, "ownerType"),
|
|
210
724
|
ownerId: optionalString(cycle, "ownerId"),
|
|
@@ -214,6 +728,7 @@ function parseCycleRun(value) {
|
|
|
214
728
|
requestedBase: optionalString(cycle, "requestedBase") ?? "develop",
|
|
215
729
|
baseBranch: optionalString(cycle, "baseBranch") ?? "develop",
|
|
216
730
|
expectedCurrentBranch: optionalString(cycle, "expectedCurrentBranch"),
|
|
731
|
+
repairHeadSha: optionalString(cycle, "repairHeadSha"),
|
|
217
732
|
attempt: requiredNumber(cycle, "attempt"),
|
|
218
733
|
model: requiredString(cycle, "model")
|
|
219
734
|
};
|
|
@@ -223,7 +738,9 @@ function parseSession(value) {
|
|
|
223
738
|
return {
|
|
224
739
|
id: requiredString(session, "id"),
|
|
225
740
|
model: requiredString(session, "model"),
|
|
226
|
-
lane: requiredString(session, "lane")
|
|
741
|
+
lane: requiredString(session, "lane"),
|
|
742
|
+
resumedFromSessionId: optionalString(session, "resumedFromSessionId"),
|
|
743
|
+
resumeProviderThreadId: optionalString(session, "resumeProviderThreadId")
|
|
227
744
|
};
|
|
228
745
|
}
|
|
229
746
|
function parseLease(value) {
|
|
@@ -251,7 +768,8 @@ function parseExecution(value) {
|
|
|
251
768
|
role: requiredString(execution, "role"),
|
|
252
769
|
lane: requiredString(execution, "lane"),
|
|
253
770
|
requiresApiKeySecretGrant: requiredBoolean(execution, "requiresApiKeySecretGrant"),
|
|
254
|
-
runnerRepoLocalPath: optionalString(execution, "runnerRepoLocalPath")
|
|
771
|
+
runnerRepoLocalPath: optionalString(execution, "runnerRepoLocalPath"),
|
|
772
|
+
effort: optionalString(execution, "effort")
|
|
255
773
|
};
|
|
256
774
|
}
|
|
257
775
|
function parseDraftPrompt(value) {
|
|
@@ -349,9 +867,49 @@ function parsePullRequest(value) {
|
|
|
349
867
|
requiredChecksTotal: requiredNumber(pullRequest, "requiredChecksTotal"),
|
|
350
868
|
unresolvedThreads: requiredNumber(pullRequest, "unresolvedThreads"),
|
|
351
869
|
humanApproved: requiredBoolean(pullRequest, "humanApproved"),
|
|
352
|
-
hasMergeConflicts: requiredBoolean(pullRequest, "hasMergeConflicts")
|
|
870
|
+
hasMergeConflicts: requiredBoolean(pullRequest, "hasMergeConflicts"),
|
|
871
|
+
headSha: optionalString(pullRequest, "headSha") ?? "",
|
|
872
|
+
reviewIssueKey: optionalString(pullRequest, "reviewIssueKey"),
|
|
873
|
+
reviewFindings: parsePullRequestReviewFindings(pullRequest.reviewFindings),
|
|
874
|
+
openWorkOrderStories: parseWorkOrderStories(pullRequest.openWorkOrderStories)
|
|
353
875
|
};
|
|
354
876
|
}
|
|
877
|
+
function parseWorkOrderStories(value) {
|
|
878
|
+
if (value === null || value === undefined)
|
|
879
|
+
return [];
|
|
880
|
+
if (!Array.isArray(value))
|
|
881
|
+
throw new Error("pullRequest.openWorkOrderStories must be an array when provided.");
|
|
882
|
+
return value.map((entry, index) => {
|
|
883
|
+
const story = asObject(entry, `pullRequest.openWorkOrderStories[${index}]`);
|
|
884
|
+
return {
|
|
885
|
+
workItemId: requiredString(story, "workItemId"),
|
|
886
|
+
title: optionalString(story, "title") ?? "",
|
|
887
|
+
acceptanceCriteria: optionalString(story, "acceptanceCriteria") ?? ""
|
|
888
|
+
};
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
function parsePullRequestReviewFindings(value) {
|
|
892
|
+
if (value === null || value === undefined)
|
|
893
|
+
return [];
|
|
894
|
+
if (!Array.isArray(value))
|
|
895
|
+
throw new Error("pullRequest.reviewFindings must be an array when provided.");
|
|
896
|
+
return value.map((entry, index) => {
|
|
897
|
+
const finding = asObject(entry, `pullRequest.reviewFindings[${index}]`);
|
|
898
|
+
return {
|
|
899
|
+
claimId: requiredString(finding, "claimId"),
|
|
900
|
+
kind: requiredString(finding, "kind"),
|
|
901
|
+
bodySha256: requiredString(finding, "bodySha256"),
|
|
902
|
+
body: optionalString(finding, "body") ?? "",
|
|
903
|
+
contentAvailable: requiredBoolean(finding, "contentAvailable"),
|
|
904
|
+
actionable: requiredBoolean(finding, "actionable"),
|
|
905
|
+
actionabilityReason: requiredString(finding, "actionabilityReason"),
|
|
906
|
+
author: optionalString(finding, "author"),
|
|
907
|
+
url: optionalString(finding, "url"),
|
|
908
|
+
path: optionalString(finding, "path"),
|
|
909
|
+
line: optionalPositiveInteger(finding, "line")
|
|
910
|
+
};
|
|
911
|
+
});
|
|
912
|
+
}
|
|
355
913
|
function parseWarmPathOverrides(value) {
|
|
356
914
|
if (value === undefined || value === null) {
|
|
357
915
|
return [];
|
|
@@ -396,6 +954,7 @@ function parseQaScope(value) {
|
|
|
396
954
|
schema_version: requiredNumber(qaScope, "schema_version"),
|
|
397
955
|
surface: parseOptionalStringArray(qaScope.surface, "qaScope.surface"),
|
|
398
956
|
required_evidence: parseOptionalStringArray(qaScope.required_evidence, "qaScope.required_evidence"),
|
|
957
|
+
required_capabilities: parseOptionalStringArray(qaScope.required_capabilities ?? qaScope.requiredCapabilities, "qaScope.required_capabilities"),
|
|
399
958
|
not_applicable: parseOptionalStringArray(qaScope.not_applicable, "qaScope.not_applicable"),
|
|
400
959
|
browser_required: optionalBoolean(qaScope, "browser_required") ?? optionalBoolean(qaScope, "browserRequired") ?? false,
|
|
401
960
|
api_required: optionalBoolean(qaScope, "api_required") ?? optionalBoolean(qaScope, "apiRequired") ?? false,
|
|
@@ -529,6 +1088,7 @@ function parseStoryQa(value) {
|
|
|
529
1088
|
}
|
|
530
1089
|
const storyQa = asObject(value, "storyQa");
|
|
531
1090
|
return {
|
|
1091
|
+
references: parseStoryQaReferences(storyQa.references),
|
|
532
1092
|
storyWorkItemId: requiredString(storyQa, "storyWorkItemId"),
|
|
533
1093
|
planId: requiredString(storyQa, "planId"),
|
|
534
1094
|
planContentHash: requiredString(storyQa, "planContentHash"),
|
|
@@ -541,6 +1101,26 @@ function parseStoryQa(value) {
|
|
|
541
1101
|
dependencies: parseStoryQaDependencies(storyQa.dependencies)
|
|
542
1102
|
};
|
|
543
1103
|
}
|
|
1104
|
+
function parseStoryQaReferences(value) {
|
|
1105
|
+
if (value === undefined || value === null)
|
|
1106
|
+
return null;
|
|
1107
|
+
const bundle = asObject(value, "storyQa.references");
|
|
1108
|
+
if (!Array.isArray(bundle.files))
|
|
1109
|
+
throw new Error("storyQa.references.files must be an array.");
|
|
1110
|
+
return {
|
|
1111
|
+
schemaVersion: requiredNumber(bundle, "schemaVersion"),
|
|
1112
|
+
cycleRunId: requiredString(bundle, "cycleRunId"),
|
|
1113
|
+
storyWorkItemId: requiredString(bundle, "storyWorkItemId"),
|
|
1114
|
+
projectId: requiredString(bundle, "projectId"), repoId: requiredString(bundle, "repoId"),
|
|
1115
|
+
planId: requiredString(bundle, "planId"), planContentHash: requiredString(bundle, "planContentHash"),
|
|
1116
|
+
effectiveAmendmentHash: requiredString(bundle, "effectiveAmendmentHash"), capturedAt: requiredString(bundle, "capturedAt"),
|
|
1117
|
+
files: bundle.files.map((entry, index) => {
|
|
1118
|
+
const file = asObject(entry, `storyQa.references.files[${index}]`);
|
|
1119
|
+
return { path: requiredString(file, "path"), body: requiredString(file, "body"),
|
|
1120
|
+
contentHash: requiredString(file, "contentHash"), provenance: requiredString(file, "provenance") };
|
|
1121
|
+
})
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
544
1124
|
function parseStoryQaChildTasks(value) {
|
|
545
1125
|
if (!Array.isArray(value)) {
|
|
546
1126
|
throw new Error("storyQa.childTasks must be an array.");
|