@loucompanion/forge-bridge 0.1.3-dev.d1ca67076aa9 → 0.1.3-dev.e7b62d8fdd33
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 +127 -0
- package/dist/bridge/bridge.base.d.ts +8 -1
- package/dist/bridge/bridge.service.d.ts +93 -3
- package/dist/bridge/bridge.service.js +1358 -47
- package/dist/bridge/bridge.types.d.ts +202 -1
- 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 +14 -2
- package/dist/cleanup/cleanup.service.js +496 -3
- package/dist/cleanup/cleanup.types.d.ts +10 -0
- package/dist/cleanup/index.d.ts +1 -1
- package/dist/cleanup/index.js +1 -1
- package/dist/cleanup/internals.d.ts +2 -2
- package/dist/cleanup/internals.js +1 -1
- package/dist/cli/cli.help.js +47 -1
- package/dist/cli/cli.service.d.ts +45 -2
- package/dist/cli/cli.service.js +378 -26
- package/dist/cli/local-config.d.ts +7 -0
- package/dist/cli/local-config.js +51 -3
- package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
- package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
- package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
- package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
- package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
- package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
- package/dist/runtime/attempt-process-supervisor.js +386 -0
- package/dist/runtime/docker-engine-probe.d.ts +83 -0
- package/dist/runtime/docker-engine-probe.js +300 -0
- package/dist/runtime/generated-output-reset.d.ts +10 -0
- package/dist/runtime/generated-output-reset.js +103 -0
- package/dist/runtime/host-listener-inspector.d.ts +46 -0
- package/dist/runtime/host-listener-inspector.js +349 -0
- package/dist/runtime/host-port-broker.d.ts +76 -0
- package/dist/runtime/host-port-broker.js +333 -0
- package/dist/runtime/host-runtime-contract.d.ts +47 -0
- package/dist/runtime/host-runtime-contract.js +700 -0
- package/dist/runtime/host-runtime-lease.d.ts +197 -0
- package/dist/runtime/host-runtime-lease.js +2203 -0
- package/dist/runtime/host-runtime-promotion.d.ts +23 -0
- package/dist/runtime/host-runtime-promotion.js +159 -0
- package/dist/runtime/memory-capacity.d.ts +68 -0
- package/dist/runtime/memory-capacity.js +121 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
- package/dist/runtime/node-host-runtime-adapter.js +284 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +231 -0
- package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
- package/dist/runtime/runtime-isolation-lease.js +158 -0
- package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
- package/dist/runtime/workspace-resource-estimator.js +174 -0
- package/dist/sandbox/command-runner.d.ts +4 -0
- package/dist/sandbox/command-runner.js +47 -0
- package/dist/sandbox/contract.d.ts +5 -0
- package/dist/sandbox/contract.js +276 -0
- package/dist/sandbox/event-budget.d.ts +11 -0
- package/dist/sandbox/event-budget.js +27 -0
- package/dist/sandbox/image-policy.d.ts +35 -0
- package/dist/sandbox/image-policy.js +247 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/lease-watchdog.d.ts +18 -0
- package/dist/sandbox/lease-watchdog.js +67 -0
- package/dist/sandbox/preflight.d.ts +84 -0
- package/dist/sandbox/preflight.js +521 -0
- package/dist/sandbox/process.d.ts +20 -0
- package/dist/sandbox/process.js +209 -0
- package/dist/sandbox/provider-auth/codex.d.ts +26 -0
- package/dist/sandbox/provider-auth/codex.js +212 -0
- package/dist/sandbox/repository-lock.d.ts +5 -0
- package/dist/sandbox/repository-lock.js +121 -0
- package/dist/sandbox/runtime.d.ts +297 -0
- package/dist/sandbox/runtime.js +2338 -0
- package/dist/sandbox/sandbox.types.d.ts +93 -0
- package/dist/sandbox/sandbox.types.js +1 -0
- package/dist/sandbox/smoke.d.ts +76 -0
- package/dist/sandbox/smoke.js +380 -0
- package/dist/sandbox/worker-supervisor.d.ts +13 -0
- package/dist/sandbox/worker-supervisor.js +36 -0
- package/dist/sandbox/worker.d.ts +1 -0
- package/dist/sandbox/worker.js +225 -0
- package/dist/sandbox/workspace.service.d.ts +13 -0
- package/dist/sandbox/workspace.service.js +72 -0
- package/dist/session/managed-attempt-tools.d.ts +21 -0
- package/dist/session/managed-attempt-tools.js +912 -0
- 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 +335 -0
- package/dist/session/phase-completion.artifacts.d.ts +17 -0
- package/dist/session/phase-completion.artifacts.js +256 -26
- package/dist/session/phase-completion.d.ts +12 -2
- package/dist/session/phase-completion.helpers.d.ts +5 -0
- package/dist/session/phase-completion.helpers.js +51 -10
- package/dist/session/phase-completion.js +14 -3
- package/dist/session/phase-completion.prompts.d.ts +3 -3
- package/dist/session/phase-completion.prompts.js +24 -8
- package/dist/session/phase-completion.runtime.d.ts +13 -1
- package/dist/session/phase-completion.runtime.js +189 -8
- package/dist/session/phase-completion.schemas.js +117 -4
- package/dist/session/phase-completion.validation.d.ts +4 -1
- package/dist/session/phase-completion.validation.js +153 -22
- package/dist/session/pre-publication-validation.d.ts +46 -0
- package/dist/session/pre-publication-validation.js +208 -0
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
- package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
- package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
- package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
- package/dist/session/provider-cli/provider-cli.deterministic.js +68 -9
- package/dist/session/provider-cli/provider-cli.process.d.ts +26 -0
- package/dist/session/provider-cli/provider-cli.process.js +474 -23
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +16 -2
- package/dist/session/provider-cli/provider-cli.prompts.js +224 -24
- package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
- 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 +1 -1
- package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
- package/dist/session/provider-cli/provider-cli.service.d.ts +26 -3
- package/dist/session/provider-cli/provider-cli.service.js +650 -108
- package/dist/session/provider-cli/provider-cli.types.d.ts +6 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
- package/dist/session/provider-cli/provider-output-state.js +91 -16
- 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.d.ts +3 -0
- package/dist/session/session-redaction.js +38 -0
- package/dist/session/session.base.d.ts +6 -1
- package/dist/session/session.service.d.ts +8 -2
- package/dist/session/session.service.js +549 -58
- package/dist/session/session.types.d.ts +124 -1
- package/dist/session/session.types.js +231 -4
- package/dist/shared/format/format-bytes.d.ts +1 -0
- package/dist/shared/format/format-bytes.js +9 -0
- package/dist/shared/path/path.d.ts +1 -0
- package/dist/shared/path/path.js +21 -1
- package/dist/shared/process/dotnet-toolchain.d.ts +14 -0
- package/dist/shared/process/dotnet-toolchain.js +179 -0
- package/dist/shared/process/host-process-recovery.d.ts +12 -0
- package/dist/shared/process/host-process-recovery.js +78 -0
- package/dist/shared/process/host-process-supervisor.d.ts +59 -0
- package/dist/shared/process/host-process-supervisor.js +330 -0
- package/dist/shared/process/process.d.ts +1 -0
- package/dist/shared/process/process.js +1 -0
- package/dist/shared/process/windows-launch.d.ts +1 -1
- package/dist/shared/process/windows-launch.js +6 -3
- package/dist/shared/provider-capabilities.d.ts +1 -0
- package/dist/shared/provider-capabilities.js +3 -0
- package/dist/shared/redaction/redaction.js +16 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +3 -2
- package/dist/worktree/artifact-injector.js +43 -4
- package/dist/worktree/dependency-warmer.d.ts +39 -1
- package/dist/worktree/dependency-warmer.js +351 -8
- package/dist/worktree/index.d.ts +2 -1
- package/dist/worktree/index.js +1 -0
- package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
- package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
- package/dist/worktree/worktree-paths.d.ts +4 -0
- package/dist/worktree/worktree-paths.js +30 -0
- package/dist/worktree/worktree.base.d.ts +3 -0
- package/dist/worktree/worktree.service.d.ts +13 -2
- package/dist/worktree/worktree.service.js +238 -47
- package/dist/worktree/worktree.types.d.ts +5 -1
- package/package.json +8 -4
- package/runtime/windows-job-supervisor.ps1 +444 -0
- package/runtime/windows-listener-owner.ps1 +33 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GitClient } from "../../shared/git/git.js";
|
|
2
2
|
import { type CompletionPhase, type CompletionToolRuntime, type DebugCompletionPhase, type QaCompletionGate } from "../phase-completion.js";
|
|
3
|
-
import type { RunOnceDispatchPacket, RunnerDispatchArtifact } from "../session.types.js";
|
|
3
|
+
import type { RunOnceDispatchPacket, RunnerDispatchArtifact, RunnerDispatchSidecar } from "../session.types.js";
|
|
4
4
|
import type { ProviderCliRunContext } from "./provider-cli.types.js";
|
|
5
5
|
export declare function emitDraftGateArtifacts(emit: ProviderCliRunContext["emit"], worktreePath: string, workItemId: string): Promise<void>;
|
|
6
6
|
export declare function emitDecomposeGateArtifacts(emit: ProviderCliRunContext["emit"], worktreePath: string, workItemId: string): Promise<void>;
|
|
@@ -18,8 +18,9 @@ export declare function isDebugCompletionPhaseForProvider(phase: CompletionPhase
|
|
|
18
18
|
export declare function emitQaArtifactRecorded(emit: ProviderCliRunContext["emit"], worktreePath: string, artifact: RunnerDispatchArtifact): Promise<void>;
|
|
19
19
|
export declare function readQaCompletionArtifactBundle(worktreePath: string, workItemId: string, ownerType?: string | null, ownerId?: string | null): Promise<RunnerDispatchArtifact | null>;
|
|
20
20
|
export declare function hasCompleteQaArtifactBundle(worktreePath: string, workItemId: string, ownerType?: string | null, ownerId?: string | null): Promise<boolean>;
|
|
21
|
-
export declare function enforceQaBrowserLaunchTargetGate(dispatch: RunOnceDispatchPacket, artifact: RunnerDispatchArtifact): void
|
|
21
|
+
export declare function enforceQaBrowserLaunchTargetGate(dispatch: RunOnceDispatchPacket, artifact: RunnerDispatchArtifact): Promise<void>;
|
|
22
22
|
export declare function validateQaBundleSidecarReferences(worktreePath: string, workItemId: string, ownerType?: string | null, ownerId?: string | null): Promise<void>;
|
|
23
|
+
export declare function readQaBundleSidecars(worktreePath: string, workItemId: string, ownerType?: string | null, ownerId?: string | null): Promise<RunnerDispatchSidecar[]>;
|
|
23
24
|
export declare function emitQaStructuredEvents(emit: ProviderCliRunContext["emit"], artifact: RunnerDispatchArtifact): Promise<void>;
|
|
24
25
|
interface QaGateArtifactSnapshot {
|
|
25
26
|
readonly gate: QaCompletionGate;
|
|
@@ -4,7 +4,10 @@ import { artifactRootFor, artifactRootForOwner, debugCompletionPhaseOrder, draft
|
|
|
4
4
|
import { ensureNoSymbolicLinkPathSegments, isPathUnderRoot, sha256File } from "../qa-sidecar-validation.js";
|
|
5
5
|
import { hasLaunchTarget } from "./provider-cli.qa-sidecars.js";
|
|
6
6
|
import { clip, isNodeError, messageFromError, normalizedString, parseJsonLine, stringField } from "./provider-output-state.js";
|
|
7
|
+
const maxTransportedQaSidecarBytes = 8 * 1024 * 1024;
|
|
8
|
+
const maxTransportedQaSidecarsTotalBytes = 16 * 1024 * 1024;
|
|
7
9
|
const developCyclePhaseOrder = ["implementation", "self_review", "validation"];
|
|
10
|
+
const nonPublishableWorktreePaths = [".forge", ".local"];
|
|
8
11
|
export async function emitDraftGateArtifacts(emit, worktreePath, workItemId) {
|
|
9
12
|
const draftArtifactFiles = {
|
|
10
13
|
draft_intent: "draft.intent.json",
|
|
@@ -147,7 +150,9 @@ export async function cleanupCompletionTools(completionTools, emit) {
|
|
|
147
150
|
}
|
|
148
151
|
export async function commitWorktreeChanges(git, worktreePath, workItemId) {
|
|
149
152
|
await git.run(worktreePath, ["add", "-A"]);
|
|
150
|
-
|
|
153
|
+
for (const reservedPath of nonPublishableWorktreePaths) {
|
|
154
|
+
await git.run(worktreePath, ["reset", "--", reservedPath]).catch(() => "");
|
|
155
|
+
}
|
|
151
156
|
const changedFiles = (await git.run(worktreePath, ["diff", "--cached", "--name-only"]))
|
|
152
157
|
.split(/\r?\n/)
|
|
153
158
|
.map((line) => line.trim())
|
|
@@ -209,12 +214,13 @@ export async function readQaCompletionArtifactBundle(worktreePath, workItemId, o
|
|
|
209
214
|
const planJson = await fs.readFile(path.join(artifactRoot, qaArtifactFileNames.plan), "utf8");
|
|
210
215
|
const evidenceJson = await fs.readFile(path.join(artifactRoot, qaArtifactFileNames.evidence), "utf8");
|
|
211
216
|
const findingsJson = await fs.readFile(path.join(artifactRoot, qaArtifactFileNames.findings), "utf8");
|
|
212
|
-
await
|
|
217
|
+
const sidecars = await readQaBundleSidecars(worktreePath, workItemId, ownerType, ownerId);
|
|
213
218
|
return {
|
|
214
219
|
...artifact,
|
|
215
220
|
planJson,
|
|
216
221
|
evidenceJson,
|
|
217
|
-
findingsJson
|
|
222
|
+
findingsJson,
|
|
223
|
+
sidecars
|
|
218
224
|
};
|
|
219
225
|
}
|
|
220
226
|
export async function hasCompleteQaArtifactBundle(worktreePath, workItemId, ownerType, ownerId) {
|
|
@@ -237,28 +243,55 @@ export async function hasCompleteQaArtifactBundle(worktreePath, workItemId, owne
|
|
|
237
243
|
}
|
|
238
244
|
return true;
|
|
239
245
|
}
|
|
240
|
-
export function enforceQaBrowserLaunchTargetGate(dispatch, artifact) {
|
|
246
|
+
export async function enforceQaBrowserLaunchTargetGate(dispatch, artifact) {
|
|
241
247
|
const qaBrowser = dispatch.qaBrowser;
|
|
242
248
|
if (!qaBrowser?.required || hasLaunchTarget(qaBrowser.launchTarget ?? null)) {
|
|
243
249
|
return;
|
|
244
250
|
}
|
|
251
|
+
const evidence = await readJsonObjectFile(path.join(path.dirname(artifact.path), qaArtifactFileNames.evidence));
|
|
252
|
+
if (hasPassingRepositoryPlaywrightEvidence(evidence)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
245
255
|
const verdict = parseJsonLine(artifact.verdictJson) ?? {};
|
|
246
256
|
const status = normalizedString(stringField(verdict, "status") ?? artifact.status ?? "");
|
|
247
257
|
const blockerReason = normalizedString(stringField(verdict, "blocker_reason") ?? "");
|
|
248
258
|
if (status !== "blocked" || blockerReason !== "missing_app_launch_target") {
|
|
249
|
-
throw new Error("QA browser testing is required but
|
|
259
|
+
throw new Error("QA browser testing is required but neither an app launch target nor passing repository Playwright evidence was provided; provider must return blocked with blocker_reason missing_app_launch_target.");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function hasPassingRepositoryPlaywrightEvidence(evidence) {
|
|
263
|
+
const passingBrowserCheck = arrayField(evidence, "browser_checks").some(check => normalizedString(stringField(check, "status") ?? "") === "pass"
|
|
264
|
+
&& Boolean(stringField(check, "url") ?? stringField(check, "route")));
|
|
265
|
+
if (!passingBrowserCheck) {
|
|
266
|
+
return false;
|
|
250
267
|
}
|
|
268
|
+
return arrayField(evidence, "commands").some(command => {
|
|
269
|
+
const status = normalizedString(stringField(command, "status") ?? "");
|
|
270
|
+
const exitCode = command.exit_code;
|
|
271
|
+
const description = [stringField(command, "command"), stringField(command, "summary")]
|
|
272
|
+
.filter((value) => Boolean(value))
|
|
273
|
+
.join(" ");
|
|
274
|
+
return status === "pass"
|
|
275
|
+
&& exitCode === 0
|
|
276
|
+
&& /(?:playwright|test:e2e|e2e(?:\s|:|$)|browser(?:\s|-)?test)/iu.test(description);
|
|
277
|
+
});
|
|
251
278
|
}
|
|
252
279
|
export async function validateQaBundleSidecarReferences(worktreePath, workItemId, ownerType, ownerId) {
|
|
280
|
+
await readQaBundleSidecars(worktreePath, workItemId, ownerType, ownerId);
|
|
281
|
+
}
|
|
282
|
+
export async function readQaBundleSidecars(worktreePath, workItemId, ownerType, ownerId) {
|
|
253
283
|
const artifactRoot = ownerType && ownerId
|
|
254
284
|
? artifactRootForOwner(worktreePath, ownerType, ownerId)
|
|
255
285
|
: artifactRootFor(worktreePath, workItemId);
|
|
256
286
|
const evidence = await readJsonObjectFile(path.join(artifactRoot, qaArtifactFileNames.evidence));
|
|
257
287
|
const sidecars = arrayField(evidence, "sidecars");
|
|
258
288
|
if (sidecars.length === 0) {
|
|
259
|
-
return;
|
|
289
|
+
return [];
|
|
260
290
|
}
|
|
261
291
|
const qaSidecarRoot = path.resolve(artifactRoot, "qa");
|
|
292
|
+
const transported = [];
|
|
293
|
+
const transportedByPath = new Map();
|
|
294
|
+
let totalBytes = 0;
|
|
262
295
|
for (const [index, sidecar] of sidecars.entries()) {
|
|
263
296
|
const sidecarPath = stringField(sidecar, "path");
|
|
264
297
|
const expectedHash = stringField(sidecar, "content_hash");
|
|
@@ -288,11 +321,36 @@ export async function validateQaBundleSidecarReferences(worktreePath, workItemId
|
|
|
288
321
|
if (stat.size !== expectedBytes) {
|
|
289
322
|
throw new Error(`QA sidecar ${index} bytes do not match the referenced file.`);
|
|
290
323
|
}
|
|
324
|
+
if (stat.size > maxTransportedQaSidecarBytes) {
|
|
325
|
+
throw new Error(`QA sidecar ${index} exceeds the ${maxTransportedQaSidecarBytes}-byte transport limit.`);
|
|
326
|
+
}
|
|
291
327
|
const actualHash = await sha256File(fullPath);
|
|
292
328
|
if (actualHash !== expectedHash.trim().toLowerCase()) {
|
|
293
329
|
throw new Error(`QA sidecar ${index} content_hash does not match the referenced file.`);
|
|
294
330
|
}
|
|
331
|
+
const canonicalPath = path.relative(worktreePath, fullPath).split(path.sep).join("/");
|
|
332
|
+
const duplicateKey = canonicalPath.toLowerCase();
|
|
333
|
+
const existing = transportedByPath.get(duplicateKey);
|
|
334
|
+
if (existing) {
|
|
335
|
+
if (existing.contentHash !== actualHash || existing.bytes !== stat.size) {
|
|
336
|
+
throw new Error(`QA sidecar ${index} conflicts with another payload for ${canonicalPath}.`);
|
|
337
|
+
}
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
totalBytes += stat.size;
|
|
341
|
+
if (totalBytes > maxTransportedQaSidecarsTotalBytes) {
|
|
342
|
+
throw new Error(`QA sidecars exceed the ${maxTransportedQaSidecarsTotalBytes}-byte total transport limit.`);
|
|
343
|
+
}
|
|
344
|
+
const content = await fs.readFile(fullPath);
|
|
345
|
+
transportedByPath.set(duplicateKey, { contentHash: actualHash, bytes: stat.size });
|
|
346
|
+
transported.push({
|
|
347
|
+
path: canonicalPath,
|
|
348
|
+
contentBase64: content.toString("base64"),
|
|
349
|
+
contentHash: actualHash,
|
|
350
|
+
bytes: stat.size
|
|
351
|
+
});
|
|
295
352
|
}
|
|
353
|
+
return transported;
|
|
296
354
|
}
|
|
297
355
|
export async function emitQaStructuredEvents(emit, artifact) {
|
|
298
356
|
const artifactRoot = path.dirname(artifact.path);
|
|
@@ -444,7 +502,7 @@ export class LiveQaArtifactEmitter {
|
|
|
444
502
|
if (!artifact) {
|
|
445
503
|
return;
|
|
446
504
|
}
|
|
447
|
-
enforceQaBrowserLaunchTargetGate(this.dispatch, artifact);
|
|
505
|
+
await enforceQaBrowserLaunchTargetGate(this.dispatch, artifact);
|
|
448
506
|
this.emittedBundle = true;
|
|
449
507
|
await emitQaArtifactRecorded(this.emit, this.worktreePath, artifact);
|
|
450
508
|
await emitQaStructuredEvents(this.emit, artifact);
|
|
@@ -2,6 +2,7 @@ import type { ProviderCliDraftContext, ProviderCliRunContext, ProviderCliRunResu
|
|
|
2
2
|
export interface ProviderCliService {
|
|
3
3
|
runImplementation(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
4
4
|
runArchitect(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
5
|
+
runCiRepair(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
5
6
|
runConflictResolve(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
6
7
|
runQa(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
7
8
|
runDraft(context: ProviderCliDraftContext): Promise<ProviderCliRunResult>;
|
|
@@ -8,5 +8,7 @@ export declare class DeterministicProviderCliService implements ProviderCliServi
|
|
|
8
8
|
runImplementation(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
9
9
|
runArchitect(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
10
10
|
runQa(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
11
|
+
runCiRepair(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
11
12
|
runConflictResolve(context: ProviderCliRunContext): Promise<ProviderCliRunResult>;
|
|
13
|
+
private runPullRequestRepair;
|
|
12
14
|
}
|
|
@@ -5,9 +5,11 @@ import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-inject
|
|
|
5
5
|
import { completionPhaseFromDispatch, debugCompletionPhaseOrder, expectedCompletionToolName, expectedDebugCompletionToolName, expectedQaCompletionToolName, qaCompletionGateOrder, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion, writeCompletionArtifact } from "../phase-completion.js";
|
|
6
6
|
import { emitArchitectStructuredEvents, emitCompletionArtifactRecorded, emitQaArtifactRecorded, emitQaGateAccepted, emitQaStructuredEvents, isDebugCompletionPhaseForProvider, nextQaGate, phaseCompleteEventType, qaArtifactRootForDispatch, readQaCompletionArtifactBundle, readQaGateArtifact } from "./provider-cli.artifacts.js";
|
|
7
7
|
import { deterministicBrowserCheck, deterministicBrowserSidecar, hasLaunchTarget, writeQaSidecarFiles } from "./provider-cli.qa-sidecars.js";
|
|
8
|
-
import { buildProviderCliCommand, formatProviderCliCommand, isPullRequestCommentsDispatch, singleLine } from "./provider-cli.prompts.js";
|
|
8
|
+
import { buildProviderCliCommand, formatProviderCliCommand, isPullRequestCheckRepairDispatch, isPullRequestCommentsDispatch, singleLine } from "./provider-cli.prompts.js";
|
|
9
9
|
import { estimatedDeterministicUsage, exactRedactionSecrets } from "./provider-cli.runtime.js";
|
|
10
10
|
import { buildProviderUsageEvent } from "./provider-usage.js";
|
|
11
|
+
import { writeCiRepairReadinessManifest } from "./ci-repair-readiness.js";
|
|
12
|
+
import { assertRepairHeadEpoch } from "./provider-cli.repair-epoch.js";
|
|
11
13
|
import { deterministicArchitectPayload, deterministicCompletionPayload, deterministicQaGatePayload } from "./provider-cli.deterministic-payloads.js";
|
|
12
14
|
export class DeterministicProviderCliService {
|
|
13
15
|
git;
|
|
@@ -202,26 +204,83 @@ export class DeterministicProviderCliService {
|
|
|
202
204
|
artifacts: [qaArtifact]
|
|
203
205
|
};
|
|
204
206
|
}
|
|
207
|
+
async runCiRepair(context) {
|
|
208
|
+
return await this.runPullRequestRepair(context, "ci_repair");
|
|
209
|
+
}
|
|
205
210
|
async runConflictResolve(context) {
|
|
211
|
+
return await this.runPullRequestRepair(context, "conflict_resolve");
|
|
212
|
+
}
|
|
213
|
+
async runPullRequestRepair(context, route) {
|
|
206
214
|
const { dispatch, worktree } = context;
|
|
215
|
+
await assertRepairHeadEpoch(context, this.git);
|
|
216
|
+
const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
|
|
207
217
|
const addressComments = isPullRequestCommentsDispatch(dispatch);
|
|
208
|
-
|
|
218
|
+
let repairArtifact = null;
|
|
219
|
+
let repairSpec = null;
|
|
220
|
+
if (repairChecks) {
|
|
221
|
+
const metadataRoot = path.join(worktree.worktreePath, ".forge", "work-items", dispatch.workItem.id);
|
|
222
|
+
await fs.mkdir(metadataRoot, { recursive: true });
|
|
223
|
+
const laneCommand = selectLaneCommand(dispatch.artifacts, dispatch.execution.lane, dispatch.cycleRun.phase);
|
|
224
|
+
if (!laneCommand)
|
|
225
|
+
throw new Error("Deterministic CI repair could not materialize the approved lane spec.");
|
|
226
|
+
const specBody = buildSpecBody(laneCommand, dispatch.workItem, "ci_repair");
|
|
227
|
+
const specPath = path.join(metadataRoot, "spec.md");
|
|
228
|
+
await fs.writeFile(specPath, specBody, "utf8");
|
|
229
|
+
await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), `${JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" })}\n`, "utf8");
|
|
230
|
+
repairSpec = { repoPath: worktree.worktreePath, path: specPath, body: specBody, producedBy: "forge-lane-command", status: "approved" };
|
|
231
|
+
await writeCiRepairReadinessManifest(dispatch, worktree.worktreePath, specPath, context.runtimeEnvironment);
|
|
232
|
+
const reference = await writeCompletionArtifact({
|
|
233
|
+
worktreePath: worktree.worktreePath,
|
|
234
|
+
workItemId: dispatch.workItem.id,
|
|
235
|
+
phase: "ci_repair",
|
|
236
|
+
producedBy: "forge-deterministic-provider",
|
|
237
|
+
}, expectedCompletionToolName("ci_repair"), deterministicCiRepairPayload());
|
|
238
|
+
repairArtifact = await readCompletionArtifact(worktree.worktreePath, dispatch.workItem.id, "ci_repair");
|
|
239
|
+
if (!repairArtifact)
|
|
240
|
+
throw new Error(`Deterministic CI repair did not produce ${reference.path}.`);
|
|
241
|
+
await emitCompletionArtifactRecorded(context.emit, worktree.worktreePath, repairArtifact);
|
|
242
|
+
}
|
|
243
|
+
await this.git.run(worktree.worktreePath, ["commit", "--allow-empty", "-m", `forge bridge ${repairChecks ? "ci repair" : addressComments ? "pr comments" : "conflict resolve"} for ${dispatch.workItem.id.slice(0, 8)}`]);
|
|
209
244
|
await context.emit(buildProviderUsageEvent(dispatch, estimatedDeterministicUsage()));
|
|
210
245
|
await context.emit({
|
|
211
246
|
role: "assistant",
|
|
212
247
|
kind: "message",
|
|
213
|
-
content:
|
|
214
|
-
? `Deterministic
|
|
215
|
-
:
|
|
216
|
-
|
|
248
|
+
content: repairChecks
|
|
249
|
+
? `Deterministic CI repair completed for ${worktree.branchName}.`
|
|
250
|
+
: addressComments
|
|
251
|
+
? `Deterministic PR comment addressing completed for ${worktree.branchName}.`
|
|
252
|
+
: `Deterministic conflict resolve completed for ${worktree.branchName}.`,
|
|
253
|
+
eventType: repairChecks ? "pr_checks_complete" : addressComments ? "pr_comments_complete" : "conflict_resolve_complete"
|
|
217
254
|
});
|
|
218
255
|
return {
|
|
219
|
-
summary:
|
|
220
|
-
? `forge-bridge
|
|
221
|
-
:
|
|
256
|
+
summary: repairChecks
|
|
257
|
+
? `forge-bridge repaired PR checks for ${worktree.branchName}.`
|
|
258
|
+
: addressComments
|
|
259
|
+
? `forge-bridge addressed PR comments for ${worktree.branchName}.`
|
|
260
|
+
: `forge-bridge resolved conflicts for ${worktree.branchName}.`,
|
|
261
|
+
spec: repairSpec,
|
|
262
|
+
artifact: repairArtifact,
|
|
263
|
+
artifacts: repairArtifact ? [repairArtifact] : [],
|
|
222
264
|
};
|
|
223
265
|
}
|
|
224
266
|
}
|
|
267
|
+
function deterministicCiRepairPayload() {
|
|
268
|
+
const command = { command: "forge deterministic ci-repair validation", exit_code: 0, status: "pass", summary: "Deterministic CI repair fixture passed." };
|
|
269
|
+
return {
|
|
270
|
+
status: "pass",
|
|
271
|
+
summary: "Deterministic CI repair handoff is ready for provider checks.",
|
|
272
|
+
failed_checks: [{ name: "CI / deterministic", provider_state: "failure", workflow: ".github/workflows/ci.yml", job: "deterministic", step: "fixture" }],
|
|
273
|
+
root_cause: { classification: "test expectation mismatch", summary: "Deterministic fixture failure repaired.", evidence: ["Fixture provider reported the expected failed check."] },
|
|
274
|
+
changed_files: [],
|
|
275
|
+
no_code_action: { action: "deterministic_fixture", reason: "The deterministic provider models the repair without a product diff." },
|
|
276
|
+
narrow_validation: [command],
|
|
277
|
+
workflow_validation: [{ step: "deterministic fixture", equivalence: "supported", command: command.command, exit_code: 0, status: "pass", summary: command.summary }],
|
|
278
|
+
broader_validation: [command],
|
|
279
|
+
remaining_risks: [],
|
|
280
|
+
provider_state: "ready_for_rerun",
|
|
281
|
+
ready_for_push: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
225
284
|
function deterministicDraftPayload(dispatch) {
|
|
226
285
|
const kind = dispatch.draft?.kind ?? "work_item";
|
|
227
286
|
if (kind === "work_order_decomposition") {
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { type LaunchCommand } from "../../shared/process/windows-launch.js";
|
|
2
|
+
export declare const PROVIDER_OUTPUT_CAPTURE_LIMIT_CHARS: number;
|
|
2
3
|
export type ProviderCliCommand = LaunchCommand;
|
|
3
4
|
export interface ProviderProcessRunRequest {
|
|
4
5
|
readonly command: string;
|
|
5
6
|
readonly args: readonly string[];
|
|
6
7
|
readonly cwd: string;
|
|
7
8
|
readonly env?: Readonly<Record<string, string>>;
|
|
9
|
+
readonly stdin?: string;
|
|
10
|
+
readonly captureOutput?: boolean;
|
|
8
11
|
}
|
|
9
12
|
export interface ProviderProcessRunResult {
|
|
10
13
|
readonly exitCode: number;
|
|
11
14
|
readonly stdout: string;
|
|
12
15
|
readonly stderr: string;
|
|
16
|
+
readonly peakMemoryBytes?: number;
|
|
17
|
+
readonly memorySampleCount?: number;
|
|
18
|
+
readonly memorySource?: string;
|
|
19
|
+
readonly memoryObservedAt?: string;
|
|
13
20
|
}
|
|
14
21
|
export interface ProviderProcessRunner {
|
|
15
22
|
run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
|
|
16
23
|
}
|
|
17
24
|
export interface ProviderProcessHandlers {
|
|
25
|
+
onStarted?(): Promise<void> | void;
|
|
18
26
|
onStdoutLine?(line: string): Promise<void> | void;
|
|
19
27
|
onStderrLine?(line: string): Promise<void> | void;
|
|
20
28
|
}
|
|
@@ -25,7 +33,25 @@ interface ProviderChildEnvironmentOptions {
|
|
|
25
33
|
}
|
|
26
34
|
export declare function buildProviderChildEnvironment(requestEnv?: Readonly<Record<string, string>>, options?: ProviderChildEnvironmentOptions): NodeJS.ProcessEnv;
|
|
27
35
|
export declare class StreamingProviderProcessRunner implements ProviderProcessRunner {
|
|
36
|
+
private readonly terminalExitGraceMs;
|
|
37
|
+
constructor(terminalExitGraceMs?: number);
|
|
28
38
|
run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
|
|
29
39
|
}
|
|
40
|
+
/** Returns aggregate resident bytes for the exact supervised Unix process group. */
|
|
41
|
+
export declare function processGroupResidentMemoryBytes(processGroupId: number, platform?: NodeJS.Platform, procRoot?: string): number | null;
|
|
42
|
+
export declare function resolveCodexNpmDirectLaunch(commandPath: string, args: readonly string[]): LaunchCommand | null;
|
|
43
|
+
/**
|
|
44
|
+
* Avoids cmd.exe's nested-quote handling for package-manager shims. That
|
|
45
|
+
* quoting loses executable paths containing spaces after the command is
|
|
46
|
+
* wrapped by the Windows Job Object supervisor.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolvePackageManagerDirectLaunch(commandPath: string, args: readonly string[]): LaunchCommand | null;
|
|
49
|
+
/**
|
|
50
|
+
* Resolves Windows commands without spawning where.exe. Runtime launches can
|
|
51
|
+
* happen while the machine is under enough process pressure for where.exe to
|
|
52
|
+
* miss its timeout; returning the unresolved package-manager name in that case
|
|
53
|
+
* makes Node fall back to a shell shim that cannot survive Job Object wrapping.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveWindowsCommandFromPath(command: string, pathValue?: string, pathExtValue?: string): string | null;
|
|
30
56
|
export declare function chooseWindowsCommandCandidate(candidates: readonly string[]): string | null;
|
|
31
57
|
export {};
|