@loucompanion/forge-bridge 0.1.3-dev.ba7d36726eda → 0.1.3-dev.bca7d0ccee53
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/dist/bridge/bridge.base.d.ts +2 -1
- package/dist/bridge/bridge.service.d.ts +8 -0
- package/dist/bridge/bridge.service.js +184 -12
- package/dist/bridge/bridge.types.d.ts +37 -0
- package/dist/cli/cli.help.js +1 -1
- package/dist/cli/cli.service.js +1 -1
- package/dist/cli/local-config.js +2 -1
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +3 -1
- package/dist/runtime/attempt-node-runtime-launcher.js +62 -11
- package/dist/runtime/attempt-process-supervisor-registry.js +1 -1
- package/dist/runtime/attempt-process-supervisor.d.ts +12 -0
- package/dist/runtime/attempt-process-supervisor.js +75 -15
- 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 +16 -0
- package/dist/runtime/host-listener-inspector.js +143 -4
- package/dist/runtime/host-runtime-contract.d.ts +9 -1
- package/dist/runtime/host-runtime-contract.js +411 -6
- package/dist/runtime/host-runtime-lease.d.ts +12 -1
- package/dist/runtime/host-runtime-lease.js +271 -50
- package/dist/runtime/host-runtime-promotion.d.ts +2 -0
- package/dist/runtime/host-runtime-promotion.js +35 -4
- package/dist/runtime/node-host-runtime-adapter.d.ts +7 -0
- package/dist/runtime/node-host-runtime-adapter.js +71 -8
- package/dist/runtime/playwright-host-runtime-adapter.js +84 -0
- package/dist/sandbox/preflight.d.ts +9 -0
- package/dist/sandbox/preflight.js +39 -4
- package/dist/sandbox/workspace.service.js +7 -2
- package/dist/session/managed-attempt-tools.d.ts +17 -0
- package/dist/session/managed-attempt-tools.js +712 -0
- package/dist/session/phase-completion.artifacts.js +9 -2
- package/dist/session/phase-completion.d.ts +11 -2
- package/dist/session/phase-completion.helpers.d.ts +5 -0
- package/dist/session/phase-completion.helpers.js +45 -9
- package/dist/session/phase-completion.js +12 -3
- package/dist/session/phase-completion.prompts.d.ts +3 -3
- package/dist/session/phase-completion.prompts.js +21 -6
- package/dist/session/phase-completion.runtime.d.ts +13 -1
- package/dist/session/phase-completion.runtime.js +183 -8
- package/dist/session/phase-completion.schemas.js +108 -3
- package/dist/session/phase-completion.validation.d.ts +4 -1
- package/dist/session/phase-completion.validation.js +144 -18
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +8 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +95 -0
- package/dist/session/provider-cli/provider-cli.deterministic.js +47 -1
- package/dist/session/provider-cli/provider-cli.process.d.ts +14 -0
- package/dist/session/provider-cli/provider-cli.process.js +176 -18
- package/dist/session/provider-cli/provider-cli.prompts.js +30 -8
- package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
- package/dist/session/provider-cli/provider-cli.service.d.ts +19 -2
- package/dist/session/provider-cli/provider-cli.service.js +390 -83
- package/dist/session/provider-cli/provider-cli.types.d.ts +3 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
- package/dist/session/provider-cli/provider-output-state.js +30 -2
- package/dist/session/session.base.d.ts +2 -1
- package/dist/session/session.service.d.ts +2 -1
- package/dist/session/session.service.js +303 -30
- package/dist/session/session.types.d.ts +38 -0
- package/dist/session/session.types.js +17 -0
- package/dist/shared/process/host-process-supervisor.d.ts +18 -1
- package/dist/shared/process/host-process-supervisor.js +94 -7
- package/dist/shared/process/process.d.ts +1 -0
- package/dist/shared/process/process.js +1 -0
- 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 +10 -1
- package/dist/worktree/dependency-warmer.js +94 -5
- package/dist/worktree/worktree.service.d.ts +1 -0
- package/dist/worktree/worktree.service.js +12 -1
- package/package.json +1 -1
- package/runtime/windows-job-supervisor.ps1 +89 -20
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { RepositoryCoordinatorLock } from "../../sandbox/repository-lock.js";
|
|
3
4
|
import { GitClient } from "../../shared/git/git.js";
|
|
4
5
|
import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-injector.js";
|
|
6
|
+
import { defaultWorktreeCoordinatorStateRoot } from "../../worktree/worktree-paths.js";
|
|
5
7
|
import { completionPhaseFromDispatch, prepareCompletionToolRuntime, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion } from "../phase-completion.js";
|
|
6
8
|
import { cleanupCompletionTools, commitWorktreeChanges, completionPhaseFromArtifact, emitArchitectStructuredEvents, emitCompletionArtifactRecorded, emitDecomposeGateArtifacts, emitDraftGateArtifacts, emitPhaseCompletionEvent, emitPhaseGateEvents, emitQaArtifactRecorded, emitQaStructuredEvents, enforceQaBrowserLaunchTargetGate, LiveArchitectArtifactEmitter, LiveCompletionArtifactEmitter, LiveQaArtifactEmitter, readCompletionArtifactsForRun, readQaCompletionArtifactBundle, summarizeStderr, waitForCompletionArtifact } from "./provider-cli.artifacts.js";
|
|
7
9
|
import { DeterministicProviderCliService } from "./provider-cli.deterministic.js";
|
|
@@ -9,34 +11,37 @@ import { buildArchitectPrompt, buildConflictResolvePrompt, buildDebugCyclePrompt
|
|
|
9
11
|
import { StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
10
12
|
import { emitProviderUsageEvent, providerProcessEnv } from "./provider-cli.runtime.js";
|
|
11
13
|
import { clip, createProviderOutputState, parseJsonLine } from "./provider-output-state.js";
|
|
14
|
+
import { writeCiRepairReadinessManifest } from "./ci-repair-readiness.js";
|
|
12
15
|
export { DeterministicProviderCliService } from "./provider-cli.deterministic.js";
|
|
13
16
|
export { buildProviderCliCommand, codexSandboxModeForPlatform, formatProviderCliCommand, formatProviderCliCommandForCheckpoint } from "./provider-cli.prompts.js";
|
|
14
|
-
export { buildProviderChildEnvironment, chooseWindowsCommandCandidate, resolveCodexNpmDirectLaunch, StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
17
|
+
export { buildProviderChildEnvironment, chooseWindowsCommandCandidate, resolveCodexNpmDirectLaunch, resolvePackageManagerDirectLaunch, resolveWindowsCommandFromPath, StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
15
18
|
export { buildWindowsLaunchCommand } from "../../shared/process/windows-launch.js";
|
|
16
|
-
async function pushWorktreeBranch(git, worktreePath, branchName, emit) {
|
|
17
|
-
|
|
18
|
-
await git.run(worktreePath, ["push", "origin", `HEAD:${branchName}`]);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
catch (err) {
|
|
22
|
-
if (!isNonFastForwardPushError(err)) {
|
|
23
|
-
throw err;
|
|
24
|
-
}
|
|
25
|
-
await emit?.({
|
|
26
|
-
role: "runner",
|
|
27
|
-
kind: "checkpoint",
|
|
28
|
-
content: `Remote branch ${branchName} moved before push; fetching latest and rebasing local changes.`,
|
|
29
|
-
eventType: "worktree_branch_push_rebase"
|
|
30
|
-
});
|
|
19
|
+
async function pushWorktreeBranch(git, worktreePath, branchName, repoPath, coordinator, signal, emit) {
|
|
20
|
+
await coordinator.withLock(repoPath, async () => {
|
|
31
21
|
try {
|
|
32
|
-
await git.run(worktreePath, ["fetch", "origin", remoteTrackingRefspec(branchName)]);
|
|
33
|
-
await git.run(worktreePath, ["rebase", `refs/remotes/origin/${branchName}`]);
|
|
34
22
|
await git.run(worktreePath, ["push", "origin", `HEAD:${branchName}`]);
|
|
23
|
+
return;
|
|
35
24
|
}
|
|
36
|
-
catch (
|
|
37
|
-
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (!isNonFastForwardPushError(err)) {
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
await emit?.({
|
|
30
|
+
role: "runner",
|
|
31
|
+
kind: "checkpoint",
|
|
32
|
+
content: `Remote branch ${branchName} moved before push; fetching latest and rebasing local changes.`,
|
|
33
|
+
eventType: "worktree_branch_push_rebase"
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
await git.run(worktreePath, ["fetch", "origin", remoteTrackingRefspec(branchName)]);
|
|
37
|
+
await git.run(worktreePath, ["rebase", `refs/remotes/origin/${branchName}`]);
|
|
38
|
+
await git.run(worktreePath, ["push", "origin", `HEAD:${branchName}`]);
|
|
39
|
+
}
|
|
40
|
+
catch (retryErr) {
|
|
41
|
+
throw new Error(`worktree_branch_integration_conflict: git push origin HEAD:${branchName} was rejected because the remote branch moved, and automatic fetch/rebase failed: ${errorMessage(retryErr)}`, { cause: retryErr });
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
|
-
}
|
|
44
|
+
}, signal);
|
|
40
45
|
}
|
|
41
46
|
function providerOwnsBranchPublication(context) {
|
|
42
47
|
return context.dispatch.sandbox?.executionMode !== "docker";
|
|
@@ -55,6 +60,179 @@ function isNonFastForwardPushError(err) {
|
|
|
55
60
|
function errorMessage(err) {
|
|
56
61
|
return err instanceof Error ? err.message : String(err);
|
|
57
62
|
}
|
|
63
|
+
const defaultProviderToolHeartbeatIntervalMs = 60_000;
|
|
64
|
+
const defaultManagedVerificationProgressIntervalMs = 30_000;
|
|
65
|
+
function compareManagedVerificationSnapshots(left, right) {
|
|
66
|
+
const leftOrder = managedVerificationStartedAt(left.startedAt) ?? Number.MAX_SAFE_INTEGER;
|
|
67
|
+
const rightOrder = managedVerificationStartedAt(right.startedAt) ?? Number.MAX_SAFE_INTEGER;
|
|
68
|
+
return leftOrder - rightOrder || left.commandId.localeCompare(right.commandId);
|
|
69
|
+
}
|
|
70
|
+
function managedVerificationStartedAt(value) {
|
|
71
|
+
const parsed = Date.parse(value ?? "");
|
|
72
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
73
|
+
}
|
|
74
|
+
export class ManagedVerificationProgressEmitter {
|
|
75
|
+
logDirectory;
|
|
76
|
+
emit;
|
|
77
|
+
intervalMs;
|
|
78
|
+
timer = null;
|
|
79
|
+
observedStatuses = new Map();
|
|
80
|
+
completedCommands = new Set();
|
|
81
|
+
constructor(logDirectory, emit, intervalMs = defaultManagedVerificationProgressIntervalMs) {
|
|
82
|
+
this.logDirectory = logDirectory;
|
|
83
|
+
this.emit = emit;
|
|
84
|
+
this.intervalMs = intervalMs;
|
|
85
|
+
}
|
|
86
|
+
start() {
|
|
87
|
+
if (!this.logDirectory?.trim() || this.timer)
|
|
88
|
+
return;
|
|
89
|
+
this.timer = setInterval(() => {
|
|
90
|
+
void this.scan().catch(() => undefined);
|
|
91
|
+
}, Math.max(1, this.intervalMs));
|
|
92
|
+
void this.scan().catch(() => undefined);
|
|
93
|
+
}
|
|
94
|
+
async stop() {
|
|
95
|
+
if (this.timer) {
|
|
96
|
+
clearInterval(this.timer);
|
|
97
|
+
this.timer = null;
|
|
98
|
+
}
|
|
99
|
+
await this.scan().catch(() => undefined);
|
|
100
|
+
}
|
|
101
|
+
async scan() {
|
|
102
|
+
if (!this.logDirectory?.trim())
|
|
103
|
+
return;
|
|
104
|
+
const directory = path.join(this.logDirectory, "managed-commands");
|
|
105
|
+
const names = await fs.readdir(directory).catch(() => []);
|
|
106
|
+
const snapshots = await Promise.all(names
|
|
107
|
+
.filter(name => name.endsWith(".receipt.json"))
|
|
108
|
+
.map(async (name) => parseManagedVerificationSnapshot(path.join(directory, name))));
|
|
109
|
+
const current = snapshots
|
|
110
|
+
.filter((entry) => entry !== null)
|
|
111
|
+
.sort(compareManagedVerificationSnapshots);
|
|
112
|
+
for (const snapshot of current) {
|
|
113
|
+
await this.emitTransition(snapshot);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async emitTransition(current) {
|
|
117
|
+
const terminal = ["pass", "fail", "timed_out", "timeout_termination_failed", "runner_error"].includes(current.status);
|
|
118
|
+
if (terminal && this.completedCommands.has(current.commandId))
|
|
119
|
+
return;
|
|
120
|
+
const previousStatus = this.observedStatuses.get(current.commandId);
|
|
121
|
+
if (previousStatus === current.status)
|
|
122
|
+
return;
|
|
123
|
+
this.observedStatuses.set(current.commandId, current.status);
|
|
124
|
+
if (terminal)
|
|
125
|
+
this.completedCommands.add(current.commandId);
|
|
126
|
+
// Starting and running are one externally meaningful state. The receipt is
|
|
127
|
+
// still refreshed on disk for UI telemetry, but repeated output/elapsed
|
|
128
|
+
// changes must not inflate the provider transcript or session history.
|
|
129
|
+
if (!terminal && previousStatus === "starting" && current.status === "running")
|
|
130
|
+
return;
|
|
131
|
+
const startedAt = managedVerificationStartedAt(current.startedAt);
|
|
132
|
+
const elapsedMs = startedAt !== null
|
|
133
|
+
? Math.max(current.elapsedMs ?? 0, Date.now() - startedAt)
|
|
134
|
+
: current.elapsedMs;
|
|
135
|
+
const elapsed = elapsedMs === null ? "an unknown duration" : formatElapsed(elapsedMs);
|
|
136
|
+
const outputTail = current.lastOutputStream === "stderr"
|
|
137
|
+
? current.stderrTail || current.stdoutTail
|
|
138
|
+
: current.stdoutTail || current.stderrTail;
|
|
139
|
+
const recovered = previousStatus === undefined && terminal;
|
|
140
|
+
await this.emit({
|
|
141
|
+
role: "runner",
|
|
142
|
+
kind: terminal && current.status !== "pass" ? "error" : "status",
|
|
143
|
+
content: terminal
|
|
144
|
+
? `Managed verification ${current.status} after ${elapsed}${recovered ? " (recovered from its durable receipt)" : ""}${outputTail ? `: ${clip(outputTail, 600)}` : "."}`
|
|
145
|
+
: `QA started managed verification ${current.commandId}. Forge is supervising it; durable progress remains available in the UI while the provider continues independent work.`,
|
|
146
|
+
eventType: terminal ? "verification_completed" : "verification_waiting",
|
|
147
|
+
data: JSON.stringify({
|
|
148
|
+
command_id: current.commandId,
|
|
149
|
+
status: current.status,
|
|
150
|
+
elapsed_ms: elapsedMs,
|
|
151
|
+
output_revision: current.outputRevision,
|
|
152
|
+
last_output_at: current.lastOutputAt,
|
|
153
|
+
recovered,
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function parseManagedVerificationSnapshot(receiptPath) {
|
|
159
|
+
try {
|
|
160
|
+
const receipt = JSON.parse(await fs.readFile(receiptPath, "utf8"));
|
|
161
|
+
const commandId = typeof receipt.command_id === "string" ? receipt.command_id : null;
|
|
162
|
+
const status = typeof receipt.status === "string" ? receipt.status : null;
|
|
163
|
+
if (!commandId || !status)
|
|
164
|
+
return null;
|
|
165
|
+
return {
|
|
166
|
+
commandId,
|
|
167
|
+
status,
|
|
168
|
+
elapsedMs: typeof receipt.elapsed_ms === "number" ? receipt.elapsed_ms : null,
|
|
169
|
+
startedAt: typeof receipt.started_at === "string" ? receipt.started_at : null,
|
|
170
|
+
outputRevision: typeof receipt.output_revision === "number" ? receipt.output_revision : null,
|
|
171
|
+
lastOutputAt: typeof receipt.last_output_at === "string" ? receipt.last_output_at : null,
|
|
172
|
+
lastOutputStream: receipt.last_output_stream === "stdout" || receipt.last_output_stream === "stderr"
|
|
173
|
+
? receipt.last_output_stream
|
|
174
|
+
: null,
|
|
175
|
+
stdoutTail: tail(typeof receipt.stdout === "string" ? receipt.stdout : ""),
|
|
176
|
+
stderrTail: tail(typeof receipt.stderr === "string" ? receipt.stderr : ""),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function tail(value) {
|
|
184
|
+
return value.trim().split(/\r?\n/u).slice(-4).join("\n");
|
|
185
|
+
}
|
|
186
|
+
function formatElapsed(milliseconds) {
|
|
187
|
+
const seconds = Math.max(0, Math.round(milliseconds / 1000));
|
|
188
|
+
const minutes = Math.floor(seconds / 60);
|
|
189
|
+
return minutes > 0 ? `${minutes}m ${seconds % 60}s` : `${seconds}s`;
|
|
190
|
+
}
|
|
191
|
+
class ProviderToolActivityHeartbeat {
|
|
192
|
+
outputState;
|
|
193
|
+
emit;
|
|
194
|
+
intervalMs;
|
|
195
|
+
timer = null;
|
|
196
|
+
constructor(outputState, emit, intervalMs) {
|
|
197
|
+
this.outputState = outputState;
|
|
198
|
+
this.emit = emit;
|
|
199
|
+
this.intervalMs = intervalMs;
|
|
200
|
+
}
|
|
201
|
+
async consume(line) {
|
|
202
|
+
const events = this.outputState.consume(line);
|
|
203
|
+
this.sync();
|
|
204
|
+
for (const event of events) {
|
|
205
|
+
await this.emit(event);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
stop() {
|
|
209
|
+
if (this.timer) {
|
|
210
|
+
clearInterval(this.timer);
|
|
211
|
+
this.timer = null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
sync() {
|
|
215
|
+
if (!this.outputState.hasActiveToolCall) {
|
|
216
|
+
this.stop();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (this.timer) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this.timer = setInterval(() => {
|
|
223
|
+
if (!this.outputState.hasActiveToolCall) {
|
|
224
|
+
this.stop();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
void this.emit({
|
|
228
|
+
role: "runner",
|
|
229
|
+
kind: "status",
|
|
230
|
+
content: "A supervised provider tool is still running.",
|
|
231
|
+
eventType: "provider_tool_heartbeat",
|
|
232
|
+
}).catch(() => undefined);
|
|
233
|
+
}, Math.max(1, this.intervalMs));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
58
236
|
const fakeWorkerModel = "server-local-fake-worker";
|
|
59
237
|
export class RoutingProviderCliService {
|
|
60
238
|
realProvider;
|
|
@@ -97,9 +275,13 @@ export class RoutingProviderCliService {
|
|
|
97
275
|
export class ProcessBackedProviderCliService {
|
|
98
276
|
processRunner;
|
|
99
277
|
git;
|
|
100
|
-
|
|
278
|
+
providerToolHeartbeatIntervalMs;
|
|
279
|
+
publicationCoordinator;
|
|
280
|
+
constructor(processRunner = new StreamingProviderProcessRunner(), git = new GitClient(), providerToolHeartbeatIntervalMs = defaultProviderToolHeartbeatIntervalMs, publicationCoordinator = new RepositoryCoordinatorLock(defaultWorktreeCoordinatorStateRoot())) {
|
|
101
281
|
this.processRunner = processRunner;
|
|
102
282
|
this.git = git;
|
|
283
|
+
this.providerToolHeartbeatIntervalMs = providerToolHeartbeatIntervalMs;
|
|
284
|
+
this.publicationCoordinator = publicationCoordinator;
|
|
103
285
|
}
|
|
104
286
|
async runDraft(context) {
|
|
105
287
|
const { dispatch } = context;
|
|
@@ -113,10 +295,10 @@ export class ProcessBackedProviderCliService {
|
|
|
113
295
|
await fs.writeFile(path.join(metadataRoot, "spec.md"), buildDraftSpec(dispatch), "utf8");
|
|
114
296
|
const stagedAttachments = await stageDraftAttachments(dispatch, metadataRoot);
|
|
115
297
|
const decomposeDraft = isDecomposeDraftDispatch(dispatch);
|
|
116
|
-
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: decomposeDraft ? "decompose" : "draft" });
|
|
298
|
+
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: decomposeDraft ? "decompose" : "draft", runtimeEnvironment: context.runtimeEnvironment });
|
|
117
299
|
const draftPrompt = buildDraftPrompt(dispatch, { attachmentReferences: stagedAttachments.textReferences });
|
|
118
300
|
const providerPrompt = buildProviderCliPrompt(dispatch, draftPrompt, context.runtimeEnvironment);
|
|
119
|
-
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt);
|
|
301
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt, usesManagedWindowsHost(dispatch));
|
|
120
302
|
const command = buildProviderCliCommand(dispatch, {
|
|
121
303
|
worktreePath: worktree.worktreePath,
|
|
122
304
|
prompt: draftPrompt,
|
|
@@ -139,7 +321,7 @@ export class ProcessBackedProviderCliService {
|
|
|
139
321
|
role: "runner",
|
|
140
322
|
kind: "checkpoint",
|
|
141
323
|
content: `Starting provider CLI Draft: ${formatProviderCliCommandForCheckpoint(command)}`,
|
|
142
|
-
eventType: "
|
|
324
|
+
eventType: "provider_cli_starting"
|
|
143
325
|
});
|
|
144
326
|
await context.emit({
|
|
145
327
|
role: "runner",
|
|
@@ -150,6 +332,7 @@ export class ProcessBackedProviderCliService {
|
|
|
150
332
|
eventType: "completion_tools_configured"
|
|
151
333
|
});
|
|
152
334
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
335
|
+
const toolHeartbeat = new ProviderToolActivityHeartbeat(outputState, context.emit, this.providerToolHeartbeatIntervalMs);
|
|
153
336
|
try {
|
|
154
337
|
const providerCancellation = new AbortController();
|
|
155
338
|
const abortProvider = () => providerCancellation.abort();
|
|
@@ -160,17 +343,23 @@ export class ProcessBackedProviderCliService {
|
|
|
160
343
|
context.signal?.addEventListener("abort", abortProvider, { once: true });
|
|
161
344
|
}
|
|
162
345
|
try {
|
|
163
|
-
const runPromise = this.processRunner.run({
|
|
346
|
+
const runPromise = (context.processRunner ?? this.processRunner).run({
|
|
164
347
|
command: command.command,
|
|
165
348
|
args: command.args,
|
|
166
349
|
cwd: worktree.worktreePath,
|
|
167
350
|
env: providerProcessEnv(dispatch, context.runtimeEnvironment),
|
|
168
351
|
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
169
352
|
}, {
|
|
353
|
+
onStarted: async () => {
|
|
354
|
+
await context.emit({
|
|
355
|
+
role: "runner",
|
|
356
|
+
kind: "checkpoint",
|
|
357
|
+
content: "Provider CLI Draft is supervised and ready.",
|
|
358
|
+
eventType: "provider_cli_started"
|
|
359
|
+
});
|
|
360
|
+
},
|
|
170
361
|
onStdoutLine: async (line) => {
|
|
171
|
-
|
|
172
|
-
await context.emit(event);
|
|
173
|
-
}
|
|
362
|
+
await toolHeartbeat.consume(line);
|
|
174
363
|
}
|
|
175
364
|
}, providerCancellation.signal);
|
|
176
365
|
const artifactWaitCancellation = new AbortController();
|
|
@@ -266,6 +455,7 @@ export class ProcessBackedProviderCliService {
|
|
|
266
455
|
}
|
|
267
456
|
}
|
|
268
457
|
finally {
|
|
458
|
+
toolHeartbeat.stop();
|
|
269
459
|
await cleanupCompletionTools(completionTools, context.emit);
|
|
270
460
|
}
|
|
271
461
|
}
|
|
@@ -284,14 +474,18 @@ export class ProcessBackedProviderCliService {
|
|
|
284
474
|
const specPath = path.join(metadataRoot, "spec.md");
|
|
285
475
|
await fs.writeFile(specPath, specBody, "utf8");
|
|
286
476
|
await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" }) + "\n", "utf8");
|
|
287
|
-
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase });
|
|
477
|
+
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase, runtimeEnvironment: context.runtimeEnvironment });
|
|
478
|
+
const implementationPrompt = debugCycle
|
|
479
|
+
? buildDebugCyclePrompt(dispatch, phase)
|
|
480
|
+
: developCycle
|
|
481
|
+
? buildDevelopCyclePrompt(dispatch, phase)
|
|
482
|
+
: buildImplementationPrompt(dispatch, phase);
|
|
483
|
+
const providerPrompt = buildProviderCliPrompt(dispatch, implementationPrompt, context.runtimeEnvironment);
|
|
484
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt, usesManagedWindowsHost(dispatch));
|
|
288
485
|
const command = buildProviderCliCommand(dispatch, {
|
|
289
486
|
worktreePath: worktree.worktreePath,
|
|
290
|
-
prompt:
|
|
291
|
-
|
|
292
|
-
: developCycle
|
|
293
|
-
? buildDevelopCyclePrompt(dispatch, phase)
|
|
294
|
-
: buildImplementationPrompt(dispatch, phase),
|
|
487
|
+
prompt: implementationPrompt,
|
|
488
|
+
promptFromStdin: pipePromptToStdin,
|
|
295
489
|
completionTools,
|
|
296
490
|
runtimeEnvironment: context.runtimeEnvironment
|
|
297
491
|
});
|
|
@@ -299,7 +493,7 @@ export class ProcessBackedProviderCliService {
|
|
|
299
493
|
role: "runner",
|
|
300
494
|
kind: "checkpoint",
|
|
301
495
|
content: `Starting provider CLI: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
302
|
-
eventType: "
|
|
496
|
+
eventType: "provider_cli_starting"
|
|
303
497
|
});
|
|
304
498
|
await context.emit({
|
|
305
499
|
role: "runner",
|
|
@@ -312,24 +506,33 @@ export class ProcessBackedProviderCliService {
|
|
|
312
506
|
eventType: "completion_tools_configured"
|
|
313
507
|
});
|
|
314
508
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
509
|
+
const toolHeartbeat = new ProviderToolActivityHeartbeat(outputState, context.emit, this.providerToolHeartbeatIntervalMs);
|
|
315
510
|
const liveCompletionEvents = new LiveCompletionArtifactEmitter(worktree.worktreePath, dispatch.workItem.id, phase, developCycle, debugCycle, context.emit);
|
|
316
511
|
let result;
|
|
317
512
|
try {
|
|
318
513
|
liveCompletionEvents.start();
|
|
319
|
-
result = await this.processRunner.run({
|
|
514
|
+
result = await (context.processRunner ?? this.processRunner).run({
|
|
320
515
|
command: command.command,
|
|
321
516
|
args: command.args,
|
|
322
517
|
cwd: worktree.worktreePath,
|
|
323
|
-
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
518
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment),
|
|
519
|
+
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
324
520
|
}, {
|
|
521
|
+
onStarted: async () => {
|
|
522
|
+
await context.emit({
|
|
523
|
+
role: "runner",
|
|
524
|
+
kind: "checkpoint",
|
|
525
|
+
content: "Provider CLI is supervised and ready.",
|
|
526
|
+
eventType: "provider_cli_started"
|
|
527
|
+
});
|
|
528
|
+
},
|
|
325
529
|
onStdoutLine: async (line) => {
|
|
326
|
-
|
|
327
|
-
await context.emit(event);
|
|
328
|
-
}
|
|
530
|
+
await toolHeartbeat.consume(line);
|
|
329
531
|
}
|
|
330
532
|
}, context.signal);
|
|
331
533
|
}
|
|
332
534
|
finally {
|
|
535
|
+
toolHeartbeat.stop();
|
|
333
536
|
await liveCompletionEvents.stop();
|
|
334
537
|
await cleanupCompletionTools(completionTools, context.emit);
|
|
335
538
|
}
|
|
@@ -346,7 +549,7 @@ export class ProcessBackedProviderCliService {
|
|
|
346
549
|
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
347
550
|
const committed = await commitWorktreeChanges(this.git, worktree.worktreePath, dispatch.workItem.id);
|
|
348
551
|
if ((developCycle || debugCycle || phase === "implementation") && providerOwnsBranchPublication(context)) {
|
|
349
|
-
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, context.emit);
|
|
552
|
+
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, worktree.repoPath, this.publicationCoordinator, context.signal, context.emit);
|
|
350
553
|
await context.emit({
|
|
351
554
|
role: "runner",
|
|
352
555
|
kind: "checkpoint",
|
|
@@ -404,10 +607,14 @@ export class ProcessBackedProviderCliService {
|
|
|
404
607
|
const specPath = path.join(metadataRoot, "spec.md");
|
|
405
608
|
await fs.writeFile(specPath, specBody, "utf8");
|
|
406
609
|
await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), JSON.stringify({ schema_version: 1, status: "pending_approval", produced_by: "forge-lane-command" }) + "\n", "utf8");
|
|
407
|
-
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: "architect" });
|
|
610
|
+
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: "architect", runtimeEnvironment: context.runtimeEnvironment });
|
|
611
|
+
const architectPrompt = buildArchitectPrompt(dispatch);
|
|
612
|
+
const providerPrompt = buildProviderCliPrompt(dispatch, architectPrompt, context.runtimeEnvironment);
|
|
613
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt, usesManagedWindowsHost(dispatch));
|
|
408
614
|
const command = buildProviderCliCommand(dispatch, {
|
|
409
615
|
worktreePath: worktree.worktreePath,
|
|
410
|
-
prompt:
|
|
616
|
+
prompt: architectPrompt,
|
|
617
|
+
promptFromStdin: pipePromptToStdin,
|
|
411
618
|
completionTools,
|
|
412
619
|
runtimeEnvironment: context.runtimeEnvironment
|
|
413
620
|
});
|
|
@@ -416,7 +623,7 @@ export class ProcessBackedProviderCliService {
|
|
|
416
623
|
role: "runner",
|
|
417
624
|
kind: "checkpoint",
|
|
418
625
|
content: `Starting provider CLI Architect: ${commandText} in ${worktree.worktreePath}`,
|
|
419
|
-
eventType: "
|
|
626
|
+
eventType: "provider_cli_starting"
|
|
420
627
|
});
|
|
421
628
|
await context.emit({
|
|
422
629
|
role: "runner",
|
|
@@ -425,24 +632,31 @@ export class ProcessBackedProviderCliService {
|
|
|
425
632
|
eventType: "completion_tools_configured"
|
|
426
633
|
});
|
|
427
634
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
635
|
+
const toolHeartbeat = new ProviderToolActivityHeartbeat(outputState, context.emit, this.providerToolHeartbeatIntervalMs);
|
|
428
636
|
const liveArchitectEvents = new LiveArchitectArtifactEmitter(worktree.worktreePath, dispatch.workItem.id, context.emit);
|
|
429
637
|
let result;
|
|
430
638
|
try {
|
|
431
639
|
liveArchitectEvents.start();
|
|
432
|
-
result = await this.processRunner.run({
|
|
640
|
+
result = await (context.processRunner ?? this.processRunner).run({
|
|
433
641
|
command: command.command,
|
|
434
642
|
args: command.args,
|
|
435
643
|
cwd: worktree.worktreePath,
|
|
436
|
-
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
644
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment),
|
|
645
|
+
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
437
646
|
}, {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
647
|
+
onStarted: async () => {
|
|
648
|
+
await context.emit({
|
|
649
|
+
role: "runner",
|
|
650
|
+
kind: "checkpoint",
|
|
651
|
+
content: "Provider CLI Architect is supervised and ready.",
|
|
652
|
+
eventType: "provider_cli_started"
|
|
653
|
+
});
|
|
654
|
+
},
|
|
655
|
+
onStdoutLine: async (line) => await toolHeartbeat.consume(line)
|
|
443
656
|
}, context.signal);
|
|
444
657
|
}
|
|
445
658
|
finally {
|
|
659
|
+
toolHeartbeat.stop();
|
|
446
660
|
await liveArchitectEvents.stop();
|
|
447
661
|
await cleanupCompletionTools(completionTools, context.emit);
|
|
448
662
|
}
|
|
@@ -496,10 +710,14 @@ export class ProcessBackedProviderCliService {
|
|
|
496
710
|
const specPath = path.join(metadataRoot, "spec.md");
|
|
497
711
|
await fs.writeFile(specPath, specBody, "utf8");
|
|
498
712
|
await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" }) + "\n", "utf8");
|
|
499
|
-
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: "qa" });
|
|
713
|
+
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: "qa", runtimeEnvironment: context.runtimeEnvironment });
|
|
714
|
+
const qaPrompt = buildQaPrompt(dispatch, specBody);
|
|
715
|
+
const providerPrompt = buildProviderCliPrompt(dispatch, qaPrompt, context.runtimeEnvironment);
|
|
716
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt, usesManagedWindowsHost(dispatch));
|
|
500
717
|
const command = buildProviderCliCommand(dispatch, {
|
|
501
718
|
worktreePath: worktree.worktreePath,
|
|
502
|
-
prompt:
|
|
719
|
+
prompt: qaPrompt,
|
|
720
|
+
promptFromStdin: pipePromptToStdin,
|
|
503
721
|
completionTools,
|
|
504
722
|
runtimeEnvironment: context.runtimeEnvironment
|
|
505
723
|
});
|
|
@@ -508,7 +726,7 @@ export class ProcessBackedProviderCliService {
|
|
|
508
726
|
role: "runner",
|
|
509
727
|
kind: "checkpoint",
|
|
510
728
|
content: `Starting provider CLI QA: ${commandText} in ${worktree.worktreePath}`,
|
|
511
|
-
eventType: "
|
|
729
|
+
eventType: "provider_cli_starting"
|
|
512
730
|
});
|
|
513
731
|
await context.emit({
|
|
514
732
|
role: "runner",
|
|
@@ -517,24 +735,34 @@ export class ProcessBackedProviderCliService {
|
|
|
517
735
|
eventType: "completion_tools_configured"
|
|
518
736
|
});
|
|
519
737
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
738
|
+
const toolHeartbeat = new ProviderToolActivityHeartbeat(outputState, context.emit, this.providerToolHeartbeatIntervalMs);
|
|
739
|
+
const verificationProgress = new ManagedVerificationProgressEmitter(context.runtimeEnvironment?.FORGE_LOG_DIR, context.emit);
|
|
520
740
|
const liveQaEvents = new LiveQaArtifactEmitter(worktree.worktreePath, dispatch.workItem.id, dispatch, context.emit);
|
|
521
741
|
let result;
|
|
522
742
|
try {
|
|
523
743
|
liveQaEvents.start();
|
|
524
|
-
|
|
744
|
+
verificationProgress.start();
|
|
745
|
+
result = await (context.processRunner ?? this.processRunner).run({
|
|
525
746
|
command: command.command,
|
|
526
747
|
args: command.args,
|
|
527
748
|
cwd: worktree.worktreePath,
|
|
528
|
-
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
749
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment),
|
|
750
|
+
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
529
751
|
}, {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
752
|
+
onStarted: async () => {
|
|
753
|
+
await context.emit({
|
|
754
|
+
role: "runner",
|
|
755
|
+
kind: "checkpoint",
|
|
756
|
+
content: "Provider CLI QA is supervised and ready.",
|
|
757
|
+
eventType: "provider_cli_started"
|
|
758
|
+
});
|
|
759
|
+
},
|
|
760
|
+
onStdoutLine: async (line) => await toolHeartbeat.consume(line)
|
|
535
761
|
}, context.signal);
|
|
536
762
|
}
|
|
537
763
|
finally {
|
|
764
|
+
toolHeartbeat.stop();
|
|
765
|
+
await verificationProgress.stop();
|
|
538
766
|
await liveQaEvents.stop();
|
|
539
767
|
await cleanupCompletionTools(completionTools, context.emit);
|
|
540
768
|
}
|
|
@@ -590,41 +818,113 @@ export class ProcessBackedProviderCliService {
|
|
|
590
818
|
const { dispatch, worktree } = context;
|
|
591
819
|
const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
|
|
592
820
|
const addressComments = !repairChecks && isPullRequestCommentsDispatch(dispatch);
|
|
821
|
+
let repairSpec = null;
|
|
822
|
+
let repairEnvironment = context.runtimeEnvironment;
|
|
823
|
+
let completionTools = null;
|
|
824
|
+
if (repairChecks) {
|
|
825
|
+
const metadataRoot = path.join(worktree.worktreePath, ".forge", "work-items", dispatch.workItem.id);
|
|
826
|
+
await fs.mkdir(metadataRoot, { recursive: true });
|
|
827
|
+
const laneCommand = selectLaneCommand(dispatch.artifacts, dispatch.execution.lane, dispatch.cycleRun.phase);
|
|
828
|
+
if (!laneCommand) {
|
|
829
|
+
throw new Error(`CI repair readiness failed: lane command for '${dispatch.execution.lane}' was not resolved, so the approved spec cannot be materialized.`);
|
|
830
|
+
}
|
|
831
|
+
const specBody = buildSpecBody(laneCommand, dispatch.workItem, "ci_repair");
|
|
832
|
+
const specPath = path.join(metadataRoot, "spec.md");
|
|
833
|
+
await fs.writeFile(specPath, specBody, "utf8");
|
|
834
|
+
await fs.writeFile(path.join(metadataRoot, "spec.meta.json"), `${JSON.stringify({ schema_version: 1, status: "approved", produced_by: "forge-lane-command" })}\n`, "utf8");
|
|
835
|
+
repairSpec = {
|
|
836
|
+
repoPath: worktree.worktreePath,
|
|
837
|
+
path: specPath,
|
|
838
|
+
body: specBody,
|
|
839
|
+
producedBy: "forge-lane-command",
|
|
840
|
+
status: "approved",
|
|
841
|
+
};
|
|
842
|
+
const readiness = await writeCiRepairReadinessManifest(dispatch, worktree.worktreePath, specPath, context.runtimeEnvironment);
|
|
843
|
+
repairEnvironment = readiness.environment;
|
|
844
|
+
completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: "ci_repair", runtimeEnvironment: repairEnvironment });
|
|
845
|
+
await context.emit({
|
|
846
|
+
role: "runner",
|
|
847
|
+
kind: "checkpoint",
|
|
848
|
+
content: `CI repair readiness contract prepared at ${path.relative(worktree.worktreePath, readiness.path).replaceAll(path.sep, "/")}; approved spec, attempt URLs, browser readiness, and local-equivalence policy are available before provider start.`,
|
|
849
|
+
eventType: "ci_repair_readiness_ready",
|
|
850
|
+
});
|
|
851
|
+
await context.emit({
|
|
852
|
+
role: "runner",
|
|
853
|
+
kind: "checkpoint",
|
|
854
|
+
content: "Forge CI repair completion tool configured; provider must call forge.complete_ci_repair before Bridge-owned commit and push.",
|
|
855
|
+
eventType: "completion_tools_configured",
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
const repairPrompt = repairChecks
|
|
859
|
+
? buildPullRequestCheckRepairPrompt(dispatch, worktree.branchName)
|
|
860
|
+
: addressComments
|
|
861
|
+
? buildPullRequestCommentsPrompt(dispatch, worktree.branchName)
|
|
862
|
+
: buildConflictResolvePrompt(dispatch, worktree.branchName);
|
|
863
|
+
const providerPrompt = buildProviderCliPrompt(dispatch, repairPrompt, repairEnvironment);
|
|
864
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt, usesManagedWindowsHost(dispatch));
|
|
593
865
|
const command = buildProviderCliCommand(dispatch, {
|
|
594
866
|
worktreePath: worktree.worktreePath,
|
|
595
|
-
prompt:
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
: buildConflictResolvePrompt(dispatch, worktree.branchName),
|
|
600
|
-
runtimeEnvironment: context.runtimeEnvironment
|
|
867
|
+
prompt: repairPrompt,
|
|
868
|
+
promptFromStdin: pipePromptToStdin,
|
|
869
|
+
completionTools: completionTools ?? undefined,
|
|
870
|
+
runtimeEnvironment: repairEnvironment
|
|
601
871
|
});
|
|
602
872
|
await context.emit({
|
|
603
873
|
role: "runner",
|
|
604
874
|
kind: "checkpoint",
|
|
605
875
|
content: `Starting provider CLI ${repairChecks ? "CI repair" : addressComments ? "PR comment address" : "conflict resolve"}: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
606
|
-
eventType: "
|
|
876
|
+
eventType: "provider_cli_starting"
|
|
607
877
|
});
|
|
608
878
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
879
|
+
const toolHeartbeat = new ProviderToolActivityHeartbeat(outputState, context.emit, this.providerToolHeartbeatIntervalMs);
|
|
880
|
+
let result;
|
|
881
|
+
try {
|
|
882
|
+
result = await (context.processRunner ?? this.processRunner).run({
|
|
883
|
+
command: command.command,
|
|
884
|
+
args: command.args,
|
|
885
|
+
cwd: worktree.worktreePath,
|
|
886
|
+
env: providerProcessEnv(dispatch, repairEnvironment),
|
|
887
|
+
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
888
|
+
}, {
|
|
889
|
+
onStarted: async () => {
|
|
890
|
+
await context.emit({
|
|
891
|
+
role: "runner",
|
|
892
|
+
kind: "checkpoint",
|
|
893
|
+
content: `Provider CLI ${repairChecks ? "CI repair" : addressComments ? "PR comment address" : "conflict resolve"} is supervised and ready.`,
|
|
894
|
+
eventType: "provider_cli_started"
|
|
895
|
+
});
|
|
896
|
+
},
|
|
897
|
+
onStdoutLine: async (line) => {
|
|
898
|
+
await toolHeartbeat.consume(line);
|
|
618
899
|
}
|
|
900
|
+
}, context.signal);
|
|
901
|
+
}
|
|
902
|
+
finally {
|
|
903
|
+
toolHeartbeat.stop();
|
|
904
|
+
if (completionTools) {
|
|
905
|
+
await cleanupCompletionTools(completionTools, context.emit);
|
|
619
906
|
}
|
|
620
|
-
}
|
|
907
|
+
}
|
|
621
908
|
if (result.exitCode !== 0) {
|
|
622
909
|
const stderr = clip(result.stderr.trim() || result.stdout.trim(), 1200);
|
|
623
910
|
throw new Error(`${command.command} exited ${result.exitCode}${stderr ? `: ${stderr}` : ""}`);
|
|
624
911
|
}
|
|
912
|
+
let repairArtifact = null;
|
|
913
|
+
if (repairChecks) {
|
|
914
|
+
repairArtifact = await readCompletionArtifact(worktree.worktreePath, dispatch.workItem.id, "ci_repair");
|
|
915
|
+
if (!repairArtifact) {
|
|
916
|
+
throw new Error("Provider CLI completed but did not call forge.complete_ci_repair. Forge did not commit or push the repair.");
|
|
917
|
+
}
|
|
918
|
+
if ((repairArtifact.status ?? "").trim().toLowerCase() !== "pass") {
|
|
919
|
+
throw new Error(`CI repair completion gate recorded ${repairArtifact.status}; Forge did not commit or push the repair.`);
|
|
920
|
+
}
|
|
921
|
+
await emitCompletionArtifactRecorded(context.emit, worktree.worktreePath, repairArtifact);
|
|
922
|
+
await emitPhaseCompletionEvent(context.emit, repairArtifact);
|
|
923
|
+
await emitPhaseGateEvents(context.emit, repairArtifact);
|
|
924
|
+
}
|
|
625
925
|
const committed = await commitWorktreeChanges(this.git, worktree.worktreePath, dispatch.workItem.id);
|
|
626
926
|
if (providerOwnsBranchPublication(context)) {
|
|
627
|
-
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, context.emit);
|
|
927
|
+
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, worktree.repoPath, this.publicationCoordinator, context.signal, context.emit);
|
|
628
928
|
}
|
|
629
929
|
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
630
930
|
await context.emit({
|
|
@@ -642,7 +942,10 @@ export class ProcessBackedProviderCliService {
|
|
|
642
942
|
? `${dispatch.execution.provider} CLI repaired PR checks on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
643
943
|
: addressComments
|
|
644
944
|
? `${dispatch.execution.provider} CLI addressed PR comments on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
645
|
-
: `${dispatch.execution.provider} CLI resolved conflicts on ${worktree.branchName} (${committed.commitSha.slice(0, 12)})
|
|
945
|
+
: `${dispatch.execution.provider} CLI resolved conflicts on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`,
|
|
946
|
+
spec: repairSpec,
|
|
947
|
+
artifact: repairArtifact,
|
|
948
|
+
artifacts: repairArtifact ? [repairArtifact] : [],
|
|
646
949
|
};
|
|
647
950
|
}
|
|
648
951
|
}
|
|
@@ -708,8 +1011,12 @@ function clipFileName(fileName, maxLength) {
|
|
|
708
1011
|
return `${stem.slice(0, maxStemLength)}${extension}`;
|
|
709
1012
|
}
|
|
710
1013
|
const providerPromptStdinThreshold = 8_000;
|
|
711
|
-
function shouldPipePromptToStdin(provider, prompt) {
|
|
712
|
-
return provider.trim().toLowerCase() === "codex"
|
|
1014
|
+
function shouldPipePromptToStdin(provider, prompt, force = false) {
|
|
1015
|
+
return provider.trim().toLowerCase() === "codex"
|
|
1016
|
+
&& (force || Buffer.byteLength(prompt, "utf8") > providerPromptStdinThreshold);
|
|
1017
|
+
}
|
|
1018
|
+
function usesManagedWindowsHost(dispatch) {
|
|
1019
|
+
return process.platform === "win32" && dispatch.sandbox?.executionMode === "host";
|
|
713
1020
|
}
|
|
714
1021
|
function isFakeWorkerModel(model) {
|
|
715
1022
|
return model.trim().toLowerCase() === fakeWorkerModel;
|