@loucompanion/forge-bridge 0.1.3-dev.d46a8e73d03d → 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.
Files changed (145) hide show
  1. package/README.md +83 -0
  2. package/dist/bridge/bridge.base.d.ts +4 -1
  3. package/dist/bridge/bridge.service.d.ts +55 -3
  4. package/dist/bridge/bridge.service.js +834 -87
  5. package/dist/bridge/bridge.types.d.ts +89 -1
  6. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  7. package/dist/bridge/runner-host-capabilities.js +93 -0
  8. package/dist/cleanup/cleanup.service.d.ts +14 -2
  9. package/dist/cleanup/cleanup.service.js +496 -3
  10. package/dist/cleanup/cleanup.types.d.ts +10 -0
  11. package/dist/cleanup/index.d.ts +1 -1
  12. package/dist/cleanup/index.js +1 -1
  13. package/dist/cleanup/internals.d.ts +2 -2
  14. package/dist/cleanup/internals.js +1 -1
  15. package/dist/cli/cli.help.js +37 -3
  16. package/dist/cli/cli.service.d.ts +33 -2
  17. package/dist/cli/cli.service.js +209 -27
  18. package/dist/cli/local-config.d.ts +5 -0
  19. package/dist/cli/local-config.js +45 -1
  20. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  21. package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
  22. package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
  23. package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
  24. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  25. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  26. package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
  27. package/dist/runtime/attempt-process-supervisor.js +386 -0
  28. package/dist/runtime/docker-engine-probe.d.ts +83 -0
  29. package/dist/runtime/docker-engine-probe.js +300 -0
  30. package/dist/runtime/generated-output-reset.d.ts +10 -0
  31. package/dist/runtime/generated-output-reset.js +103 -0
  32. package/dist/runtime/host-listener-inspector.d.ts +46 -0
  33. package/dist/runtime/host-listener-inspector.js +349 -0
  34. package/dist/runtime/host-port-broker.d.ts +76 -0
  35. package/dist/runtime/host-port-broker.js +333 -0
  36. package/dist/runtime/host-runtime-contract.d.ts +47 -0
  37. package/dist/runtime/host-runtime-contract.js +700 -0
  38. package/dist/runtime/host-runtime-lease.d.ts +197 -0
  39. package/dist/runtime/host-runtime-lease.js +2203 -0
  40. package/dist/runtime/host-runtime-promotion.d.ts +23 -0
  41. package/dist/runtime/host-runtime-promotion.js +159 -0
  42. package/dist/runtime/memory-capacity.d.ts +68 -0
  43. package/dist/runtime/memory-capacity.js +121 -0
  44. package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
  45. package/dist/runtime/node-host-runtime-adapter.js +284 -0
  46. package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
  47. package/dist/runtime/playwright-host-runtime-adapter.js +231 -0
  48. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  49. package/dist/runtime/runtime-isolation-lease.js +158 -0
  50. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  51. package/dist/runtime/workspace-resource-estimator.js +174 -0
  52. package/dist/sandbox/preflight.d.ts +45 -1
  53. package/dist/sandbox/preflight.js +299 -34
  54. package/dist/sandbox/process.js +125 -40
  55. package/dist/sandbox/repository-lock.js +41 -3
  56. package/dist/sandbox/runtime.d.ts +66 -3
  57. package/dist/sandbox/runtime.js +670 -105
  58. package/dist/sandbox/sandbox.types.d.ts +10 -0
  59. package/dist/sandbox/smoke.d.ts +76 -0
  60. package/dist/sandbox/smoke.js +380 -0
  61. package/dist/sandbox/worker-supervisor.js +1 -1
  62. package/dist/sandbox/worker.js +2 -5
  63. package/dist/sandbox/workspace.service.d.ts +1 -0
  64. package/dist/sandbox/workspace.service.js +28 -5
  65. package/dist/session/managed-attempt-tools.d.ts +21 -0
  66. package/dist/session/managed-attempt-tools.js +912 -0
  67. package/dist/session/managed-build-coordinator.d.ts +5 -0
  68. package/dist/session/managed-build-coordinator.js +105 -0
  69. package/dist/session/managed-build-receipt.d.ts +21 -0
  70. package/dist/session/managed-build-receipt.js +335 -0
  71. package/dist/session/phase-completion.artifacts.d.ts +17 -0
  72. package/dist/session/phase-completion.artifacts.js +233 -24
  73. package/dist/session/phase-completion.d.ts +12 -2
  74. package/dist/session/phase-completion.helpers.d.ts +5 -0
  75. package/dist/session/phase-completion.helpers.js +51 -10
  76. package/dist/session/phase-completion.js +14 -3
  77. package/dist/session/phase-completion.prompts.d.ts +3 -3
  78. package/dist/session/phase-completion.prompts.js +24 -8
  79. package/dist/session/phase-completion.runtime.d.ts +13 -1
  80. package/dist/session/phase-completion.runtime.js +189 -8
  81. package/dist/session/phase-completion.schemas.js +117 -4
  82. package/dist/session/phase-completion.validation.d.ts +4 -1
  83. package/dist/session/phase-completion.validation.js +153 -22
  84. package/dist/session/pre-publication-validation.d.ts +46 -0
  85. package/dist/session/pre-publication-validation.js +208 -0
  86. package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
  87. package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
  88. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
  89. package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
  90. package/dist/session/provider-cli/provider-cli.deterministic.js +49 -1
  91. package/dist/session/provider-cli/provider-cli.process.d.ts +25 -0
  92. package/dist/session/provider-cli/provider-cli.process.js +463 -34
  93. package/dist/session/provider-cli/provider-cli.prompts.d.ts +4 -1
  94. package/dist/session/provider-cli/provider-cli.prompts.js +108 -25
  95. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
  96. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  97. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  98. package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
  99. package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
  100. package/dist/session/provider-cli/provider-cli.service.d.ts +22 -2
  101. package/dist/session/provider-cli/provider-cli.service.js +524 -100
  102. package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
  103. package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
  104. package/dist/session/provider-cli/provider-output-state.js +91 -16
  105. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  106. package/dist/session/provider-cli/provider-usage.js +28 -5
  107. package/dist/session/session-redaction.d.ts +3 -0
  108. package/dist/session/session-redaction.js +38 -0
  109. package/dist/session/session.base.d.ts +6 -1
  110. package/dist/session/session.service.d.ts +8 -2
  111. package/dist/session/session.service.js +522 -41
  112. package/dist/session/session.types.d.ts +72 -0
  113. package/dist/session/session.types.js +74 -3
  114. package/dist/shared/format/format-bytes.d.ts +1 -0
  115. package/dist/shared/format/format-bytes.js +9 -0
  116. package/dist/shared/path/path.d.ts +1 -0
  117. package/dist/shared/path/path.js +21 -1
  118. package/dist/shared/process/dotnet-toolchain.d.ts +14 -0
  119. package/dist/shared/process/dotnet-toolchain.js +179 -0
  120. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  121. package/dist/shared/process/host-process-recovery.js +78 -0
  122. package/dist/shared/process/host-process-supervisor.d.ts +59 -0
  123. package/dist/shared/process/host-process-supervisor.js +330 -0
  124. package/dist/shared/process/process.d.ts +1 -0
  125. package/dist/shared/process/process.js +1 -0
  126. package/dist/shared/redaction/redaction.js +12 -2
  127. package/dist/version.d.ts +1 -1
  128. package/dist/version.js +1 -1
  129. package/dist/worktree/artifact-injector.d.ts +3 -2
  130. package/dist/worktree/artifact-injector.js +43 -4
  131. package/dist/worktree/dependency-warmer.d.ts +28 -1
  132. package/dist/worktree/dependency-warmer.js +308 -9
  133. package/dist/worktree/index.d.ts +2 -1
  134. package/dist/worktree/index.js +1 -0
  135. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  136. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  137. package/dist/worktree/worktree-paths.d.ts +4 -0
  138. package/dist/worktree/worktree-paths.js +30 -0
  139. package/dist/worktree/worktree.base.d.ts +3 -0
  140. package/dist/worktree/worktree.service.d.ts +13 -2
  141. package/dist/worktree/worktree.service.js +238 -47
  142. package/dist/worktree/worktree.types.d.ts +5 -1
  143. package/package.json +5 -3
  144. package/runtime/windows-job-supervisor.ps1 +444 -0
  145. 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
- await git.run(worktreePath, ["reset", "--", ".forge"]).catch(() => "");
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 validateQaBundleSidecarReferences(worktreePath, workItemId, ownerType, ownerId);
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 no app launch target was provided; provider must return blocked with blocker_reason missing_app_launch_target.");
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);
@@ -8,6 +8,8 @@ import { deterministicBrowserCheck, deterministicBrowserSidecar, hasLaunchTarget
8
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;
@@ -210,8 +212,34 @@ export class DeterministicProviderCliService {
210
212
  }
211
213
  async runPullRequestRepair(context, route) {
212
214
  const { dispatch, worktree } = context;
215
+ await assertRepairHeadEpoch(context, this.git);
213
216
  const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
214
217
  const addressComments = isPullRequestCommentsDispatch(dispatch);
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
+ }
215
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)}`]);
216
244
  await context.emit(buildProviderUsageEvent(dispatch, estimatedDeterministicUsage()));
217
245
  await context.emit({
@@ -229,10 +257,30 @@ export class DeterministicProviderCliService {
229
257
  ? `forge-bridge repaired PR checks for ${worktree.branchName}.`
230
258
  : addressComments
231
259
  ? `forge-bridge addressed PR comments for ${worktree.branchName}.`
232
- : `forge-bridge resolved conflicts for ${worktree.branchName}.`
260
+ : `forge-bridge resolved conflicts for ${worktree.branchName}.`,
261
+ spec: repairSpec,
262
+ artifact: repairArtifact,
263
+ artifacts: repairArtifact ? [repairArtifact] : [],
233
264
  };
234
265
  }
235
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
+ }
236
284
  function deterministicDraftPayload(dispatch) {
237
285
  const kind = dispatch.draft?.kind ?? "work_item";
238
286
  if (kind === "work_order_decomposition") {
@@ -1,4 +1,5 @@
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;
@@ -6,16 +7,22 @@ export interface ProviderProcessRunRequest {
6
7
  readonly cwd: string;
7
8
  readonly env?: Readonly<Record<string, string>>;
8
9
  readonly stdin?: string;
10
+ readonly captureOutput?: boolean;
9
11
  }
10
12
  export interface ProviderProcessRunResult {
11
13
  readonly exitCode: number;
12
14
  readonly stdout: string;
13
15
  readonly stderr: string;
16
+ readonly peakMemoryBytes?: number;
17
+ readonly memorySampleCount?: number;
18
+ readonly memorySource?: string;
19
+ readonly memoryObservedAt?: string;
14
20
  }
15
21
  export interface ProviderProcessRunner {
16
22
  run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
17
23
  }
18
24
  export interface ProviderProcessHandlers {
25
+ onStarted?(): Promise<void> | void;
19
26
  onStdoutLine?(line: string): Promise<void> | void;
20
27
  onStderrLine?(line: string): Promise<void> | void;
21
28
  }
@@ -26,7 +33,25 @@ interface ProviderChildEnvironmentOptions {
26
33
  }
27
34
  export declare function buildProviderChildEnvironment(requestEnv?: Readonly<Record<string, string>>, options?: ProviderChildEnvironmentOptions): NodeJS.ProcessEnv;
28
35
  export declare class StreamingProviderProcessRunner implements ProviderProcessRunner {
36
+ private readonly terminalExitGraceMs;
37
+ constructor(terminalExitGraceMs?: number);
29
38
  run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
30
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;
31
56
  export declare function chooseWindowsCommandCandidate(candidates: readonly string[]): string | null;
32
57
  export {};