@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,5 +1,7 @@
|
|
|
1
1
|
import { redactRunnerDispatchEvent, redactRunOnceResultPacket } from "./session-redaction.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { RoutingProviderCliService } from "./provider-cli/provider-cli.service.js";
|
|
4
|
+
import { ForgeFailureError, waitKindForEvent, waitWakeCondition } from "./session.types.js";
|
|
3
5
|
import { GitWorktreeService } from "../worktree/worktree.service.js";
|
|
4
6
|
import { SandboxWorkspaceService } from "../sandbox/workspace.service.js";
|
|
5
7
|
import { HostRuntimeAdmissionExpiredError, hostRuntimeEventResources, } from "../runtime/host-runtime-lease.js";
|
|
@@ -37,9 +39,12 @@ export class BridgeSessionService {
|
|
|
37
39
|
let cleanupFailure = null;
|
|
38
40
|
try {
|
|
39
41
|
throwIfAborted(signal);
|
|
40
|
-
await this.hostRuntime.invalidatePromotion?.(request.repoId);
|
|
41
42
|
await this.hostRuntime.acquire(dispatch, signal);
|
|
42
43
|
admissionAcquired = true;
|
|
44
|
+
// Readiness probes acquire background-exclusive project admission. Only
|
|
45
|
+
// invalidate the prior proof after that fence is held so a user lane can
|
|
46
|
+
// never observe the proof disappear underneath shared admission.
|
|
47
|
+
await this.hostRuntime.invalidatePromotion?.(request.repoId, "discovery");
|
|
43
48
|
assertReadinessProbeStartupBudget(request.expiresAt);
|
|
44
49
|
worktree = await this.worktrees.prepare({
|
|
45
50
|
repo: dispatch.repo,
|
|
@@ -51,7 +56,10 @@ export class BridgeSessionService {
|
|
|
51
56
|
lane: "runtime_readiness",
|
|
52
57
|
phase: "readiness_probe",
|
|
53
58
|
allowedRepoRoot: request.repoRoot,
|
|
54
|
-
|
|
59
|
+
// Readiness probes are disposable infrastructure checks, not agent lanes.
|
|
60
|
+
// Always remove their worktree even when the bridge is configured to retain
|
|
61
|
+
// failed agent worktrees for inspection.
|
|
62
|
+
cleanupWorktrees: dispatch.serverLocal.cleanupWorktrees,
|
|
55
63
|
artifacts: [],
|
|
56
64
|
});
|
|
57
65
|
assertReadinessProbeStartupBudget(request.expiresAt);
|
|
@@ -120,18 +128,39 @@ export class BridgeSessionService {
|
|
|
120
128
|
validateApiKeySecretGrant(dispatch);
|
|
121
129
|
const redactionSecrets = exactSecrets(dispatch);
|
|
122
130
|
const events = [];
|
|
131
|
+
let waitObservationVersion = 0;
|
|
132
|
+
const waitFirstObservedAt = new Map();
|
|
133
|
+
const waitObservation = (eventType, queuePosition) => {
|
|
134
|
+
const occurredAt = new Date().toISOString();
|
|
135
|
+
const kind = waitKindForEvent(eventType);
|
|
136
|
+
const firstObservedAt = waitFirstObservedAt.get(kind) ?? occurredAt;
|
|
137
|
+
waitFirstObservedAt.set(kind, firstObservedAt);
|
|
138
|
+
waitObservationVersion += 1;
|
|
139
|
+
const wait = {
|
|
140
|
+
schemaVersion: 2,
|
|
141
|
+
kind,
|
|
142
|
+
scope: "project",
|
|
143
|
+
resourceKeys: [`project-runtime:${dispatch.repo.id}`],
|
|
144
|
+
...(queuePosition === undefined ? {} : { queuePosition }),
|
|
145
|
+
wakeCondition: waitWakeCondition(kind),
|
|
146
|
+
firstObservedAt,
|
|
147
|
+
observationVersion: waitObservationVersion,
|
|
148
|
+
contractOrigin: "bridge_v2",
|
|
149
|
+
};
|
|
150
|
+
return { occurredAt, wait };
|
|
151
|
+
};
|
|
123
152
|
const emit = async (event) => {
|
|
124
153
|
if (signal?.aborted) {
|
|
125
154
|
return;
|
|
126
155
|
}
|
|
127
|
-
const redacted = redactRunnerDispatchEvent(event, redactionSecrets);
|
|
156
|
+
const redacted = redactRunnerDispatchEvent({ ...event, eventId: event.eventId ?? randomUUID() }, redactionSecrets);
|
|
128
157
|
if (redacted.eventType !== "provider_tool_heartbeat") {
|
|
129
158
|
events.push(redacted);
|
|
130
159
|
}
|
|
131
160
|
await sink.emit(redacted);
|
|
132
161
|
};
|
|
133
162
|
const emitLifecycle = async (event) => {
|
|
134
|
-
const redacted = redactRunnerDispatchEvent(event, redactionSecrets);
|
|
163
|
+
const redacted = redactRunnerDispatchEvent({ ...event, eventId: event.eventId ?? randomUUID() }, redactionSecrets);
|
|
135
164
|
events.push(redacted);
|
|
136
165
|
await sink.emit(redacted);
|
|
137
166
|
};
|
|
@@ -147,14 +176,31 @@ export class BridgeSessionService {
|
|
|
147
176
|
let hostLease = null;
|
|
148
177
|
try {
|
|
149
178
|
if (dispatch.sandbox?.executionMode === "host" && this.hostRuntime) {
|
|
179
|
+
const capacity = this.hostRuntime.snapshot?.() ?? null;
|
|
180
|
+
const projectCapacity = capacity?.projects.find(project => project.repoId === dispatch.repo.id) ?? null;
|
|
181
|
+
const admissionWait = waitObservation("host_runtime_admission_waiting", capacity ? capacity.waitingAttempts + 1 : undefined);
|
|
150
182
|
await emitLifecycle({
|
|
151
183
|
role: "runner",
|
|
152
184
|
kind: "checkpoint",
|
|
153
|
-
content:
|
|
185
|
+
content: capacity
|
|
186
|
+
? `Waiting for host-runtime admission before preparing the worktree (queue position ${capacity.waitingAttempts + 1}; runner ${capacity.activeAttempts} active, ${capacity.reservedAttempts} reserved; project ${projectCapacity?.activeAttempts ?? 0} active, ${projectCapacity?.waitingAttempts ?? 0} already waiting). This wait can be caused by the project serialization policy, readiness proof, or resource headroom; Forge will continue automatically when admission is available.`
|
|
187
|
+
: "Waiting for host-runtime admission before preparing the worktree. Forge will continue automatically when admission is available.",
|
|
154
188
|
eventType: "host_runtime_admission_waiting",
|
|
189
|
+
...admissionWait,
|
|
190
|
+
data: capacity ? JSON.stringify({
|
|
191
|
+
queue_position: capacity.waitingAttempts + 1,
|
|
192
|
+
runner_active_attempts: capacity.activeAttempts,
|
|
193
|
+
runner_reserved_attempts: capacity.reservedAttempts,
|
|
194
|
+
runner_waiting_attempts: capacity.waitingAttempts,
|
|
195
|
+
project_active_attempts: projectCapacity?.activeAttempts ?? 0,
|
|
196
|
+
project_reserved_attempts: projectCapacity?.reservedAttempts ?? 0,
|
|
197
|
+
project_waiting_attempts: projectCapacity?.waitingAttempts ?? 0,
|
|
198
|
+
project_safety_class: projectCapacity?.safetyClass ?? "unknown",
|
|
199
|
+
}) : undefined,
|
|
155
200
|
});
|
|
156
201
|
try {
|
|
157
|
-
hostSingletonKeys = await this.hostRuntime.
|
|
202
|
+
hostSingletonKeys = await (this.hostRuntime.acquirePreparation?.(dispatch, signal)
|
|
203
|
+
?? this.hostRuntime.acquire(dispatch, signal));
|
|
158
204
|
}
|
|
159
205
|
catch (error) {
|
|
160
206
|
if (error instanceof HostRuntimeAdmissionExpiredError) {
|
|
@@ -168,13 +214,17 @@ export class BridgeSessionService {
|
|
|
168
214
|
throw error;
|
|
169
215
|
}
|
|
170
216
|
throwIfAborted(signal);
|
|
217
|
+
const admissionAcquired = waitObservation("host_runtime_admission_acquired");
|
|
171
218
|
await emitLifecycle({
|
|
172
219
|
role: "runner",
|
|
173
220
|
kind: "checkpoint",
|
|
174
|
-
content:
|
|
175
|
-
?
|
|
176
|
-
:
|
|
221
|
+
content: this.hostRuntime.acquirePreparation
|
|
222
|
+
? "Acquired worktree-preparation capacity. Prepared runtime verification must finish before agent admission."
|
|
223
|
+
: hostSingletonKeys.length > 0
|
|
224
|
+
? `Acquired managed host singleton ${hostSingletonKeys.join(", ")}.`
|
|
225
|
+
: "Observed assigned-port evidence permits provisional parallel host admission; the prepared runtime contract must still match before launch.",
|
|
177
226
|
eventType: "host_runtime_admission_acquired",
|
|
227
|
+
...admissionAcquired,
|
|
178
228
|
});
|
|
179
229
|
}
|
|
180
230
|
try {
|
|
@@ -191,20 +241,28 @@ export class BridgeSessionService {
|
|
|
191
241
|
cleanupWorktrees: dispatch.serverLocal.cleanupWorktrees,
|
|
192
242
|
artifacts: dispatch.artifacts,
|
|
193
243
|
priorPhaseArtifacts: dispatch.priorPhaseArtifacts,
|
|
244
|
+
storyQa: dispatch.storyQa,
|
|
245
|
+
referenceRunId: dispatch.sandbox?.dispatchAttemptId ?? dispatch.cycleRun.id,
|
|
246
|
+
referenceCycleRunId: dispatch.cycleRun.id,
|
|
247
|
+
effectivePlanAmendmentHash: dispatch.cycleRun.effectivePlanAmendmentHash,
|
|
194
248
|
qaRework: dispatch.qaRework,
|
|
195
|
-
warmPathOverrides: dispatch.warmPathOverrides
|
|
249
|
+
warmPathOverrides: dispatch.warmPathOverrides,
|
|
250
|
+
applicationRuntimeRequired: dispatch.runtimeIntent.applicationRuntimeRequired,
|
|
251
|
+
preserveExistingState: Boolean(dispatch.session.resumedFromSessionId)
|
|
196
252
|
});
|
|
197
253
|
}
|
|
198
254
|
catch (error) {
|
|
199
|
-
throw new
|
|
255
|
+
throw new ForgeFailureError("worktree_prepare_failed", `worktree_prepare_failed: ${failureMessage(error)}`, "bridge", { cause: error });
|
|
200
256
|
}
|
|
201
257
|
let providerDispatch = withPreparedQaReworkPacket(dispatch, worktree.qaReworkPacketPath);
|
|
202
258
|
throwIfAborted(signal);
|
|
203
259
|
await emit({
|
|
204
260
|
role: "runner",
|
|
205
261
|
kind: "checkpoint",
|
|
206
|
-
content:
|
|
207
|
-
|
|
262
|
+
content: dispatch.session.resumedFromSessionId
|
|
263
|
+
? `Reattached to worktree ${worktree.branchName} without resetting source or clearing attempt artifacts.`
|
|
264
|
+
: `Prepared worktree ${worktree.branchName}.`,
|
|
265
|
+
eventType: dispatch.session.resumedFromSessionId ? "worktree_reattached" : "worktree_prepared"
|
|
208
266
|
});
|
|
209
267
|
if ((worktree.hydratedPriorPhaseArtifactCount ?? 0) > 0) {
|
|
210
268
|
await emit({
|
|
@@ -218,7 +276,17 @@ export class BridgeSessionService {
|
|
|
218
276
|
await emitDependencyWarmEvents(worktree.dependencyWarm, emit);
|
|
219
277
|
}
|
|
220
278
|
if (dispatch.sandbox?.executionMode === "host" && this.hostRuntime) {
|
|
221
|
-
hostLease = await this.hostRuntime.start(dispatch, worktree.worktreePath, signal)
|
|
279
|
+
hostLease = await this.hostRuntime.start(dispatch, worktree.worktreePath, signal, async (checkpoint) => {
|
|
280
|
+
const observation = waitObservation(checkpoint.code);
|
|
281
|
+
await emitLifecycle({
|
|
282
|
+
role: "runner",
|
|
283
|
+
kind: "checkpoint",
|
|
284
|
+
content: checkpoint.message,
|
|
285
|
+
eventType: checkpoint.code,
|
|
286
|
+
...observation,
|
|
287
|
+
data: JSON.stringify({ expires_at: checkpoint.expiresAt }),
|
|
288
|
+
});
|
|
289
|
+
});
|
|
222
290
|
await emitLifecycle({
|
|
223
291
|
role: "runner",
|
|
224
292
|
kind: "checkpoint",
|
|
@@ -238,7 +306,15 @@ export class BridgeSessionService {
|
|
|
238
306
|
}),
|
|
239
307
|
});
|
|
240
308
|
const unconfiguredPortCount = hostLease.lease.resources.ports.length;
|
|
241
|
-
hostLease = await this.hostRuntime.startConfiguredProject(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken, signal)
|
|
309
|
+
hostLease = await this.hostRuntime.startConfiguredProject(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken, signal, async (checkpoint) => {
|
|
310
|
+
await emitLifecycle({
|
|
311
|
+
role: "runner",
|
|
312
|
+
kind: "checkpoint",
|
|
313
|
+
content: checkpoint.message,
|
|
314
|
+
eventType: checkpoint.code,
|
|
315
|
+
data: checkpoint.data ? JSON.stringify(checkpoint.data) : undefined,
|
|
316
|
+
});
|
|
317
|
+
});
|
|
242
318
|
if (hostLease.lease.resources.ports.length > unconfiguredPortCount) {
|
|
243
319
|
const assigned = hostLease.lease.resources.ports.at(-1);
|
|
244
320
|
await emitLifecycle({
|
|
@@ -267,7 +343,7 @@ export class BridgeSessionService {
|
|
|
267
343
|
throwIfAborted(signal);
|
|
268
344
|
const draftArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
|
|
269
345
|
return redactRunOnceResultPacket({
|
|
270
|
-
schemaVersion:
|
|
346
|
+
schemaVersion: 2,
|
|
271
347
|
exitCode: 0,
|
|
272
348
|
summary: run.summary,
|
|
273
349
|
events,
|
|
@@ -308,18 +384,21 @@ export class BridgeSessionService {
|
|
|
308
384
|
}
|
|
309
385
|
catch (error) {
|
|
310
386
|
const message = failureMessage(error);
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
387
|
+
if (error instanceof ForgeFailureError
|
|
388
|
+
&& (error.failureCode === "attempt_process_supervisor_start_failed"
|
|
389
|
+
|| error.failureCode === "attempt_process_supervisor_start_timeout")) {
|
|
390
|
+
throw new ForgeFailureError("provider_start_unavailable", `provider_start_unavailable: ${message}`, "bridge", { cause: error });
|
|
314
391
|
}
|
|
315
392
|
throw error;
|
|
316
393
|
}
|
|
317
394
|
throwIfAborted(signal);
|
|
318
395
|
const phaseArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
|
|
319
396
|
return redactRunOnceResultPacket({
|
|
320
|
-
schemaVersion:
|
|
397
|
+
schemaVersion: 2,
|
|
321
398
|
exitCode: 0,
|
|
322
399
|
summary: run.summary,
|
|
400
|
+
resultCommitSha: run.resultCommitSha ?? null,
|
|
401
|
+
publishedCommitSha: run.publishedCommitSha ?? null,
|
|
323
402
|
events,
|
|
324
403
|
spec: run.spec ?? null,
|
|
325
404
|
artifact: run.artifact ?? null,
|
|
@@ -328,6 +407,25 @@ export class BridgeSessionService {
|
|
|
328
407
|
}
|
|
329
408
|
finally {
|
|
330
409
|
if (hostLease && dispatch.sandbox) {
|
|
410
|
+
const providerMemory = this.hostRuntime?.providerMemoryTelemetry?.(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken);
|
|
411
|
+
if (providerMemory) {
|
|
412
|
+
await emitLifecycle({
|
|
413
|
+
role: "runner",
|
|
414
|
+
kind: "checkpoint",
|
|
415
|
+
content: `Observed managed-host provider process-tree peak memory ${formatMemoryBytes(providerMemory.peakMemoryBytes)} from ${providerMemory.sampleCount} sample${providerMemory.sampleCount === 1 ? "" : "s"}.`,
|
|
416
|
+
eventType: "sandbox_peak_memory_observed",
|
|
417
|
+
occurredAt: providerMemory.observedAt,
|
|
418
|
+
data: JSON.stringify({
|
|
419
|
+
peakMemoryBytes: providerMemory.peakMemoryBytes,
|
|
420
|
+
sampleCount: providerMemory.sampleCount,
|
|
421
|
+
source: providerMemory.source,
|
|
422
|
+
containers: [{
|
|
423
|
+
role: "agent",
|
|
424
|
+
peakMemoryBytes: providerMemory.peakMemoryBytes,
|
|
425
|
+
}],
|
|
426
|
+
}),
|
|
427
|
+
}).catch(() => undefined);
|
|
428
|
+
}
|
|
331
429
|
const teardownAt = new Date().toISOString();
|
|
332
430
|
await emitLifecycle({
|
|
333
431
|
role: "runner",
|
|
@@ -351,7 +449,7 @@ export class BridgeSessionService {
|
|
|
351
449
|
await emitLifecycle({
|
|
352
450
|
role: "runner",
|
|
353
451
|
kind: "checkpoint",
|
|
354
|
-
content: `Released host runtime lease ${released.leaseId}.`,
|
|
452
|
+
content: `Released host runtime lease ${released.leaseId}. Diagnostics remain runner-local.`,
|
|
355
453
|
eventType: "sandbox_teardown_completed",
|
|
356
454
|
occurredAt: released.releasedAt,
|
|
357
455
|
}).catch(() => undefined);
|
|
@@ -370,7 +468,7 @@ export class BridgeSessionService {
|
|
|
370
468
|
if (!hostLease && hostSingletonKeys && dispatch.sandbox) {
|
|
371
469
|
await this.hostRuntime?.release(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken).catch(() => undefined);
|
|
372
470
|
}
|
|
373
|
-
if (worktree)
|
|
471
|
+
if (worktree && !isPauseCancellation(signal))
|
|
374
472
|
await this.worktrees.release(worktree);
|
|
375
473
|
}
|
|
376
474
|
}
|
|
@@ -378,6 +476,13 @@ export class BridgeSessionService {
|
|
|
378
476
|
function failureMessage(error) {
|
|
379
477
|
return error instanceof Error ? error.message : String(error);
|
|
380
478
|
}
|
|
479
|
+
function formatMemoryBytes(bytes) {
|
|
480
|
+
return `${(bytes / (1024 ** 2)).toFixed(bytes >= 1024 ** 3 ? 0 : 1)} MiB`;
|
|
481
|
+
}
|
|
482
|
+
function isPauseCancellation(signal) {
|
|
483
|
+
return signal?.aborted === true
|
|
484
|
+
&& String(signal.reason ?? "").trim().toLowerCase() === "session pause requested";
|
|
485
|
+
}
|
|
381
486
|
function readinessProbeDispatch(request) {
|
|
382
487
|
return {
|
|
383
488
|
schemaVersion: 2,
|
|
@@ -433,7 +538,7 @@ function readinessProbeDispatch(request) {
|
|
|
433
538
|
runnerRepoLocalPath: request.runnerRepoLocalPath,
|
|
434
539
|
},
|
|
435
540
|
runtimeIntent: { executionClass: "managed_runtime", applicationRuntimeRequired: true },
|
|
436
|
-
serverLocal: { repoRoot: request.repoRoot, cleanupWorktrees:
|
|
541
|
+
serverLocal: { repoRoot: request.repoRoot, cleanupWorktrees: true },
|
|
437
542
|
sandbox: {
|
|
438
543
|
dispatchAttemptId: request.requestId,
|
|
439
544
|
executionLeaseId: request.requestId,
|
|
@@ -552,6 +657,23 @@ function withPreparedQaReworkPacket(dispatch, packetPath) {
|
|
|
552
657
|
};
|
|
553
658
|
}
|
|
554
659
|
async function emitDependencyWarmEvents(warm, emit) {
|
|
660
|
+
for (const receipt of warm.receipts) {
|
|
661
|
+
await emit({
|
|
662
|
+
role: "runner",
|
|
663
|
+
kind: "checkpoint",
|
|
664
|
+
content: receipt.cacheHit
|
|
665
|
+
? `Reused verified ${receipt.ecosystem} dependency preparation receipt ${receipt.fingerprint} in ${receipt.packageRoot}.`
|
|
666
|
+
: `Published ${receipt.ecosystem} dependency preparation receipt ${receipt.fingerprint} in ${receipt.packageRoot}.`,
|
|
667
|
+
eventType: receipt.cacheHit ? "dependencies_preparation_cached" : "dependencies_preparation_receipt_published",
|
|
668
|
+
data: JSON.stringify({
|
|
669
|
+
ecosystem: receipt.ecosystem,
|
|
670
|
+
package_root: receipt.packageRoot,
|
|
671
|
+
cache_hit: receipt.cacheHit,
|
|
672
|
+
fingerprint: receipt.fingerprint,
|
|
673
|
+
receipt_path: receipt.receiptPath,
|
|
674
|
+
}),
|
|
675
|
+
});
|
|
676
|
+
}
|
|
555
677
|
if (warm.linked.length > 0) {
|
|
556
678
|
await emit({
|
|
557
679
|
role: "runner",
|
|
@@ -94,6 +94,7 @@ export interface DispatchQaScope {
|
|
|
94
94
|
readonly schema_version: number;
|
|
95
95
|
readonly surface: readonly string[];
|
|
96
96
|
readonly required_evidence: readonly string[];
|
|
97
|
+
readonly required_capabilities?: readonly string[];
|
|
97
98
|
readonly not_applicable: readonly string[];
|
|
98
99
|
readonly browser_required: boolean;
|
|
99
100
|
readonly api_required: boolean;
|
|
@@ -152,6 +153,7 @@ export interface DispatchStoryDevelopContext {
|
|
|
152
153
|
readonly dependencyWorkItemIds: readonly string[];
|
|
153
154
|
}
|
|
154
155
|
export interface DispatchStoryQaContext {
|
|
156
|
+
readonly references?: DispatchStoryQaReferenceBundle | null;
|
|
155
157
|
readonly storyWorkItemId: string;
|
|
156
158
|
readonly planId: string;
|
|
157
159
|
readonly planContentHash: string;
|
|
@@ -163,6 +165,24 @@ export interface DispatchStoryQaContext {
|
|
|
163
165
|
readonly childTasks: readonly DispatchStoryQaChildTaskContext[];
|
|
164
166
|
readonly dependencies: readonly DispatchStoryQaDependencyContext[];
|
|
165
167
|
}
|
|
168
|
+
export interface DispatchStoryQaReferenceBundle {
|
|
169
|
+
readonly schemaVersion: number;
|
|
170
|
+
readonly cycleRunId: string;
|
|
171
|
+
readonly storyWorkItemId: string;
|
|
172
|
+
readonly projectId: string;
|
|
173
|
+
readonly repoId: string;
|
|
174
|
+
readonly planId: string;
|
|
175
|
+
readonly planContentHash: string;
|
|
176
|
+
readonly effectiveAmendmentHash: string;
|
|
177
|
+
readonly capturedAt: string;
|
|
178
|
+
readonly files: readonly DispatchStoryQaReferenceFile[];
|
|
179
|
+
}
|
|
180
|
+
export interface DispatchStoryQaReferenceFile {
|
|
181
|
+
readonly path: string;
|
|
182
|
+
readonly body: string;
|
|
183
|
+
readonly contentHash: string;
|
|
184
|
+
readonly provenance: string;
|
|
185
|
+
}
|
|
166
186
|
export interface DispatchStoryQaChildTaskContext {
|
|
167
187
|
readonly workItemId: string;
|
|
168
188
|
readonly title: string;
|
|
@@ -263,6 +283,28 @@ export interface DispatchPullRequestContext {
|
|
|
263
283
|
readonly unresolvedThreads: number;
|
|
264
284
|
readonly humanApproved: boolean;
|
|
265
285
|
readonly hasMergeConflicts: boolean;
|
|
286
|
+
readonly headSha: string;
|
|
287
|
+
readonly reviewIssueKey?: string | null;
|
|
288
|
+
readonly reviewFindings: readonly DispatchPullRequestReviewFinding[];
|
|
289
|
+
readonly openWorkOrderStories: readonly DispatchWorkOrderStory[];
|
|
290
|
+
}
|
|
291
|
+
export interface DispatchWorkOrderStory {
|
|
292
|
+
readonly workItemId: string;
|
|
293
|
+
readonly title: string;
|
|
294
|
+
readonly acceptanceCriteria: string;
|
|
295
|
+
}
|
|
296
|
+
export interface DispatchPullRequestReviewFinding {
|
|
297
|
+
readonly claimId: string;
|
|
298
|
+
readonly kind: string;
|
|
299
|
+
readonly bodySha256: string;
|
|
300
|
+
readonly body: string;
|
|
301
|
+
readonly contentAvailable: boolean;
|
|
302
|
+
readonly actionable: boolean;
|
|
303
|
+
readonly actionabilityReason: string;
|
|
304
|
+
readonly author?: string | null;
|
|
305
|
+
readonly url?: string | null;
|
|
306
|
+
readonly path?: string | null;
|
|
307
|
+
readonly line?: number | null;
|
|
266
308
|
}
|
|
267
309
|
export interface DispatchResolvedArtifact {
|
|
268
310
|
readonly kind: string;
|
|
@@ -275,6 +317,7 @@ export interface DispatchResolvedArtifact {
|
|
|
275
317
|
readonly event?: string | null;
|
|
276
318
|
}
|
|
277
319
|
export interface DispatchWorkItem {
|
|
320
|
+
readonly projectId?: string | null;
|
|
278
321
|
readonly id: string;
|
|
279
322
|
readonly kind: string;
|
|
280
323
|
readonly title: string;
|
|
@@ -292,6 +335,7 @@ export interface DispatchRepo {
|
|
|
292
335
|
readonly serverLocalPath?: string | null;
|
|
293
336
|
}
|
|
294
337
|
export interface DispatchCycleRun {
|
|
338
|
+
readonly effectivePlanAmendmentHash?: string | null;
|
|
295
339
|
readonly id: string;
|
|
296
340
|
readonly ownerType?: string | null;
|
|
297
341
|
readonly ownerId?: string | null;
|
|
@@ -301,6 +345,7 @@ export interface DispatchCycleRun {
|
|
|
301
345
|
readonly requestedBase: string;
|
|
302
346
|
readonly baseBranch: string;
|
|
303
347
|
readonly expectedCurrentBranch?: string | null;
|
|
348
|
+
readonly repairHeadSha?: string | null;
|
|
304
349
|
readonly attempt: number;
|
|
305
350
|
readonly model: string;
|
|
306
351
|
}
|
|
@@ -308,6 +353,8 @@ export interface DispatchSession {
|
|
|
308
353
|
readonly id: string;
|
|
309
354
|
readonly model: string;
|
|
310
355
|
readonly lane: string;
|
|
356
|
+
readonly resumedFromSessionId?: string | null;
|
|
357
|
+
readonly resumeProviderThreadId?: string | null;
|
|
311
358
|
}
|
|
312
359
|
export interface DispatchLease {
|
|
313
360
|
readonly id: string;
|
|
@@ -330,6 +377,8 @@ export interface DispatchExecution {
|
|
|
330
377
|
readonly lane: string;
|
|
331
378
|
readonly requiresApiKeySecretGrant: boolean;
|
|
332
379
|
readonly runnerRepoLocalPath?: string | null;
|
|
380
|
+
/** Reasoning effort from the Agents tab; null means the provider CLI's default. */
|
|
381
|
+
readonly effort?: string | null;
|
|
333
382
|
}
|
|
334
383
|
export interface ApiKeySecretGrant {
|
|
335
384
|
readonly grantId: string;
|
|
@@ -347,6 +396,8 @@ export interface DispatchServerLocalConfig {
|
|
|
347
396
|
readonly cleanupWorktrees: boolean;
|
|
348
397
|
}
|
|
349
398
|
export interface RunnerDispatchEvent {
|
|
399
|
+
/** Stable across callback retries; persisted atomically with the session event. */
|
|
400
|
+
readonly eventId?: string | null;
|
|
350
401
|
readonly role: string;
|
|
351
402
|
readonly kind: string;
|
|
352
403
|
readonly content: string;
|
|
@@ -355,6 +406,22 @@ export interface RunnerDispatchEvent {
|
|
|
355
406
|
readonly occurredAt?: string | null;
|
|
356
407
|
/** Optional structured telemetry payload (JSON string), e.g. token usage for provider_usage events. */
|
|
357
408
|
readonly data?: string | null;
|
|
409
|
+
/** Versioned nonterminal wait evidence. Legacy writers omit this field. */
|
|
410
|
+
readonly wait?: WaitEnvelope | null;
|
|
411
|
+
}
|
|
412
|
+
export interface WaitEnvelope {
|
|
413
|
+
readonly schemaVersion: 2;
|
|
414
|
+
readonly kind: string;
|
|
415
|
+
readonly scope: string;
|
|
416
|
+
readonly resourceKeys: readonly string[];
|
|
417
|
+
readonly queuePosition?: number | null;
|
|
418
|
+
readonly blockerSessionId?: string | null;
|
|
419
|
+
readonly blockerTitle?: string | null;
|
|
420
|
+
readonly wakeCondition: string;
|
|
421
|
+
readonly retryAt?: string | null;
|
|
422
|
+
readonly firstObservedAt: string;
|
|
423
|
+
readonly observationVersion: number;
|
|
424
|
+
readonly contractOrigin: "bridge_v2";
|
|
358
425
|
}
|
|
359
426
|
export interface RunnerDispatchSpec {
|
|
360
427
|
readonly repoPath: string;
|
|
@@ -376,12 +443,74 @@ export interface RunnerDispatchArtifact {
|
|
|
376
443
|
readonly producedBy: string;
|
|
377
444
|
readonly status: string;
|
|
378
445
|
readonly phase?: string | null;
|
|
446
|
+
/**
|
|
447
|
+
* QA sidecars are transported with the terminal result because managed
|
|
448
|
+
* worktrees may be released before the API mirrors the result. The API
|
|
449
|
+
* validates these bytes against the canonical evidence metadata and stores
|
|
450
|
+
* only that metadata.
|
|
451
|
+
*/
|
|
452
|
+
readonly sidecars?: readonly RunnerDispatchSidecar[] | null;
|
|
453
|
+
}
|
|
454
|
+
export interface RunnerDispatchSidecar {
|
|
455
|
+
readonly path: string;
|
|
456
|
+
readonly contentBase64: string;
|
|
457
|
+
readonly contentHash: string;
|
|
458
|
+
readonly bytes: number;
|
|
459
|
+
}
|
|
460
|
+
export type FailureCategory = "domain" | "provider" | "transport" | "cancellation" | "ownership" | "resource" | "unknown";
|
|
461
|
+
export type FailurePhase = "offer" | "build" | "prepare" | "agent" | "validation" | "import" | "teardown" | "unknown";
|
|
462
|
+
export type FailureSource = "bridge" | "provider" | "sandbox" | "unknown";
|
|
463
|
+
export type FailureRetryability = "retryable" | "non_retryable" | "policy_required" | "unknown";
|
|
464
|
+
export type FailureOperatorAction = "none" | "retry_dispatch" | "retry_with_policy" | "inspect_failure" | "fix_input" | "fix_environment" | "reauthenticate" | "restore_ownership";
|
|
465
|
+
export interface FailureCause {
|
|
466
|
+
readonly code: string;
|
|
467
|
+
readonly message: string;
|
|
468
|
+
readonly source: FailureSource;
|
|
469
|
+
}
|
|
470
|
+
export interface FailureEnvelope {
|
|
471
|
+
readonly schemaVersion: 2;
|
|
472
|
+
readonly code: string;
|
|
473
|
+
readonly message: string;
|
|
474
|
+
readonly category: FailureCategory;
|
|
475
|
+
readonly phase: FailurePhase;
|
|
476
|
+
readonly source: FailureSource;
|
|
477
|
+
readonly retryability: FailureRetryability;
|
|
478
|
+
readonly operatorAction: FailureOperatorAction;
|
|
479
|
+
readonly causalChain: readonly FailureCause[];
|
|
480
|
+
readonly occurredAt: string;
|
|
481
|
+
readonly contractOrigin: "bridge_v2";
|
|
482
|
+
}
|
|
483
|
+
export interface FailureFacts {
|
|
484
|
+
readonly code: string;
|
|
485
|
+
readonly category: FailureCategory;
|
|
486
|
+
readonly phase: FailurePhase;
|
|
487
|
+
readonly source: FailureSource;
|
|
488
|
+
readonly retryability: FailureRetryability;
|
|
489
|
+
readonly operatorAction: FailureOperatorAction;
|
|
490
|
+
readonly causalChain?: readonly FailureCause[];
|
|
491
|
+
}
|
|
492
|
+
export interface FailurePolicy {
|
|
493
|
+
readonly code: string;
|
|
494
|
+
readonly category: FailureCategory;
|
|
495
|
+
readonly phase: FailurePhase;
|
|
496
|
+
readonly retryability: FailureRetryability;
|
|
497
|
+
readonly operatorAction: FailureOperatorAction;
|
|
498
|
+
readonly allowedSources: readonly FailureSource[];
|
|
499
|
+
readonly known: boolean;
|
|
500
|
+
}
|
|
501
|
+
export declare class ForgeFailureError extends Error {
|
|
502
|
+
readonly failureCode: string;
|
|
503
|
+
readonly failureSource: FailureSource;
|
|
504
|
+
constructor(failureCode: string, message: string, failureSource?: FailureSource, options?: ErrorOptions);
|
|
379
505
|
}
|
|
380
506
|
export interface RunOnceResultPacket {
|
|
381
|
-
readonly schemaVersion: 1;
|
|
507
|
+
readonly schemaVersion: 1 | 2;
|
|
382
508
|
readonly exitCode: number;
|
|
383
509
|
readonly summary: string;
|
|
510
|
+
readonly resultCommitSha?: string | null;
|
|
511
|
+
readonly publishedCommitSha?: string | null;
|
|
384
512
|
readonly events: readonly RunnerDispatchEvent[];
|
|
513
|
+
readonly failure?: FailureEnvelope | null;
|
|
385
514
|
readonly spec?: RunnerDispatchSpec | null;
|
|
386
515
|
readonly artifact?: RunnerDispatchArtifact | null;
|
|
387
516
|
readonly artifacts?: readonly RunnerDispatchArtifact[] | null;
|
|
@@ -391,6 +520,9 @@ export interface RunnerEventLine {
|
|
|
391
520
|
readonly event: RunnerDispatchEvent;
|
|
392
521
|
}
|
|
393
522
|
export declare function parseRunOnceDispatchPacket(value: unknown): RunOnceDispatchPacket;
|
|
523
|
+
export declare function parseRunOnceResultPacket(value: unknown): RunOnceResultPacket;
|
|
524
|
+
export declare function waitKindForEvent(eventType: string): string;
|
|
525
|
+
export declare function waitWakeCondition(kind: string): string;
|
|
394
526
|
export interface HostRuntimeReadinessProbeRequest {
|
|
395
527
|
readonly requestId: string;
|
|
396
528
|
readonly fencingToken: string;
|
|
@@ -423,4 +555,17 @@ export interface HostRuntimeReadinessQaLaunchTarget {
|
|
|
423
555
|
readonly managedRuntimePort: string;
|
|
424
556
|
readonly routes: readonly string[];
|
|
425
557
|
}
|
|
426
|
-
export declare function failureResult(summary: string, events?: readonly RunnerDispatchEvent[]): RunOnceResultPacket;
|
|
558
|
+
export declare function failureResult(summary: string, events?: readonly RunnerDispatchEvent[], facts?: FailureFacts): RunOnceResultPacket;
|
|
559
|
+
export declare function createFailureEnvelope(message: string, facts: FailureFacts, occurredAt?: string): FailureEnvelope;
|
|
560
|
+
export declare function failureFactsFromError(error: unknown, options?: {
|
|
561
|
+
readonly cancelled?: boolean;
|
|
562
|
+
readonly source?: FailureSource;
|
|
563
|
+
}): FailureFacts;
|
|
564
|
+
export declare function failureFactsForCode(code: string, source?: FailureSource): FailureFacts;
|
|
565
|
+
export declare function failurePolicyForCode(code: string): FailurePolicy;
|
|
566
|
+
/**
|
|
567
|
+
* Rebuilds an error graph at a privileged execution boundary without
|
|
568
|
+
* retaining the raw error or any raw recursive cause. Forge failure metadata
|
|
569
|
+
* survives so the P7 taxonomy remains authoritative after sanitization.
|
|
570
|
+
*/
|
|
571
|
+
export declare function sanitizeFailureError(error: unknown, exactSecrets?: readonly string[]): Error;
|