@loucompanion/forge-bridge 0.1.3-dev.b3bce6ba65a6 → 0.1.3-dev.ba7d36726eda
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 +78 -0
- package/dist/bridge/bridge.base.d.ts +5 -1
- package/dist/bridge/bridge.service.d.ts +52 -3
- package/dist/bridge/bridge.service.js +776 -19
- package/dist/bridge/bridge.types.d.ts +131 -1
- package/dist/cleanup/cleanup.service.d.ts +13 -2
- package/dist/cleanup/cleanup.service.js +435 -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 +46 -0
- package/dist/cli/cli.service.d.ts +42 -2
- package/dist/cli/cli.service.js +350 -23
- package/dist/cli/local-config.d.ts +4 -0
- package/dist/cli/local-config.js +39 -0
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +15 -0
- package/dist/runtime/attempt-node-runtime-launcher.js +50 -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 +74 -0
- package/dist/runtime/attempt-process-supervisor.js +290 -0
- package/dist/runtime/host-listener-inspector.d.ts +28 -0
- package/dist/runtime/host-listener-inspector.js +185 -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 +39 -0
- package/dist/runtime/host-runtime-contract.js +295 -0
- package/dist/runtime/host-runtime-lease.d.ts +122 -0
- package/dist/runtime/host-runtime-lease.js +1241 -0
- package/dist/runtime/host-runtime-promotion.d.ts +21 -0
- package/dist/runtime/host-runtime-promotion.js +128 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +44 -0
- package/dist/runtime/node-host-runtime-adapter.js +205 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +20 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +136 -0
- package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
- package/dist/runtime/runtime-isolation-lease.js +158 -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 +45 -0
- package/dist/sandbox/preflight.js +300 -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 +119 -0
- package/dist/sandbox/runtime.d.ts +297 -0
- package/dist/sandbox/runtime.js +2338 -0
- package/dist/sandbox/sandbox.types.d.ts +86 -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 +67 -0
- package/dist/session/phase-completion.artifacts.js +23 -2
- package/dist/session/phase-completion.helpers.js +6 -1
- package/dist/session/phase-completion.runtime.js +3 -1
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +1 -1
- package/dist/session/provider-cli/provider-cli.artifacts.js +28 -4
- 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 +20 -9
- package/dist/session/provider-cli/provider-cli.process.d.ts +3 -0
- package/dist/session/provider-cli/provider-cli.process.js +65 -14
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +14 -2
- package/dist/session/provider-cli/provider-cli.prompts.js +99 -15
- package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
- package/dist/session/provider-cli/provider-cli.runtime.js +3 -2
- package/dist/session/provider-cli/provider-cli.service.d.ts +4 -1
- package/dist/session/provider-cli/provider-cli.service.js +167 -28
- package/dist/session/provider-cli/provider-cli.types.d.ts +2 -0
- package/dist/session/session-redaction.d.ts +3 -0
- package/dist/session/session-redaction.js +23 -0
- package/dist/session/session.base.d.ts +4 -0
- package/dist/session/session.service.d.ts +6 -1
- package/dist/session/session.service.js +184 -38
- package/dist/session/session.types.d.ts +58 -1
- package/dist/session/session.types.js +172 -2
- package/dist/shared/path/path.js +3 -1
- 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 +40 -0
- package/dist/shared/process/host-process-supervisor.js +195 -0
- package/dist/shared/provider-capabilities.d.ts +1 -0
- package/dist/shared/provider-capabilities.js +3 -0
- package/dist/shared/redaction/redaction.js +6 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/index.d.ts +1 -0
- 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 +6 -2
- package/dist/worktree/worktree.service.js +60 -43
- package/dist/worktree/worktree.types.d.ts +3 -1
- package/package.json +6 -3
- package/runtime/windows-job-supervisor.ps1 +287 -0
- package/runtime/windows-listener-owner.ps1 +33 -0
|
@@ -5,14 +5,56 @@ import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-inject
|
|
|
5
5
|
import { completionPhaseFromDispatch, prepareCompletionToolRuntime, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion } from "../phase-completion.js";
|
|
6
6
|
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
7
|
import { DeterministicProviderCliService } from "./provider-cli.deterministic.js";
|
|
8
|
-
import { buildArchitectPrompt, buildConflictResolvePrompt, buildDebugCyclePrompt, buildDevelopCyclePrompt, buildDraftPrompt, buildDraftSpec, buildImplementationPrompt, buildProviderCliCommand, durableDraftInput, buildPullRequestCommentsPrompt, buildQaPrompt, formatProviderCliCommandForCheckpoint, isDecomposeDraftDispatch, isPullRequestCommentsDispatch } from "./provider-cli.prompts.js";
|
|
8
|
+
import { buildArchitectPrompt, buildConflictResolvePrompt, buildDebugCyclePrompt, buildDevelopCyclePrompt, buildDraftPrompt, buildDraftSpec, buildImplementationPrompt, buildProviderCliPrompt, buildProviderCliCommand, durableDraftInput, buildPullRequestCheckRepairPrompt, buildPullRequestCommentsPrompt, buildQaPrompt, formatProviderCliCommandForCheckpoint, isDecomposeDraftDispatch, isPullRequestCheckRepairDispatch, isPullRequestCommentsDispatch } from "./provider-cli.prompts.js";
|
|
9
9
|
import { StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
10
10
|
import { emitProviderUsageEvent, providerProcessEnv } from "./provider-cli.runtime.js";
|
|
11
11
|
import { clip, createProviderOutputState, parseJsonLine } from "./provider-output-state.js";
|
|
12
12
|
export { DeterministicProviderCliService } from "./provider-cli.deterministic.js";
|
|
13
13
|
export { buildProviderCliCommand, codexSandboxModeForPlatform, formatProviderCliCommand, formatProviderCliCommandForCheckpoint } from "./provider-cli.prompts.js";
|
|
14
|
-
export { buildProviderChildEnvironment, chooseWindowsCommandCandidate, StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
14
|
+
export { buildProviderChildEnvironment, chooseWindowsCommandCandidate, resolveCodexNpmDirectLaunch, StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
15
15
|
export { buildWindowsLaunchCommand } from "../../shared/process/windows-launch.js";
|
|
16
|
+
async function pushWorktreeBranch(git, worktreePath, branchName, emit) {
|
|
17
|
+
try {
|
|
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
|
+
});
|
|
31
|
+
try {
|
|
32
|
+
await git.run(worktreePath, ["fetch", "origin", remoteTrackingRefspec(branchName)]);
|
|
33
|
+
await git.run(worktreePath, ["rebase", `refs/remotes/origin/${branchName}`]);
|
|
34
|
+
await git.run(worktreePath, ["push", "origin", `HEAD:${branchName}`]);
|
|
35
|
+
}
|
|
36
|
+
catch (retryErr) {
|
|
37
|
+
throw new Error(`git push origin HEAD:${branchName} was rejected because the remote branch moved, and automatic fetch/rebase failed: ${errorMessage(retryErr)}`, { cause: retryErr });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function providerOwnsBranchPublication(context) {
|
|
42
|
+
return context.dispatch.sandbox?.executionMode !== "docker";
|
|
43
|
+
}
|
|
44
|
+
function remoteTrackingRefspec(branchName) {
|
|
45
|
+
return `refs/heads/${branchName}:refs/remotes/origin/${branchName}`;
|
|
46
|
+
}
|
|
47
|
+
function isNonFastForwardPushError(err) {
|
|
48
|
+
const message = errorMessage(err).toLowerCase();
|
|
49
|
+
return message.includes("non-fast-forward")
|
|
50
|
+
|| message.includes("fetch first")
|
|
51
|
+
|| message.includes("tip of your current branch is behind")
|
|
52
|
+
|| message.includes("updates were rejected because the tip of your current branch is behind")
|
|
53
|
+
|| message.includes("stale info");
|
|
54
|
+
}
|
|
55
|
+
function errorMessage(err) {
|
|
56
|
+
return err instanceof Error ? err.message : String(err);
|
|
57
|
+
}
|
|
16
58
|
const fakeWorkerModel = "server-local-fake-worker";
|
|
17
59
|
export class RoutingProviderCliService {
|
|
18
60
|
realProvider;
|
|
@@ -31,6 +73,11 @@ export class RoutingProviderCliService {
|
|
|
31
73
|
? await this.deterministicProvider.runArchitect(context)
|
|
32
74
|
: await this.realProvider.runArchitect(context);
|
|
33
75
|
}
|
|
76
|
+
async runCiRepair(context) {
|
|
77
|
+
return isFakeWorkerModel(context.dispatch.execution.model)
|
|
78
|
+
? await this.deterministicProvider.runCiRepair(context)
|
|
79
|
+
: await this.realProvider.runCiRepair(context);
|
|
80
|
+
}
|
|
34
81
|
async runConflictResolve(context) {
|
|
35
82
|
return isFakeWorkerModel(context.dispatch.execution.model)
|
|
36
83
|
? await this.deterministicProvider.runConflictResolve(context)
|
|
@@ -64,13 +111,20 @@ export class ProcessBackedProviderCliService {
|
|
|
64
111
|
await fs.mkdir(metadataRoot, { recursive: true });
|
|
65
112
|
await fs.writeFile(path.join(metadataRoot, "draft-input.json"), JSON.stringify(durableDraftInput(dispatch), null, 2) + "\n", "utf8");
|
|
66
113
|
await fs.writeFile(path.join(metadataRoot, "spec.md"), buildDraftSpec(dispatch), "utf8");
|
|
114
|
+
const stagedAttachments = await stageDraftAttachments(dispatch, metadataRoot);
|
|
67
115
|
const decomposeDraft = isDecomposeDraftDispatch(dispatch);
|
|
68
116
|
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree, phase: decomposeDraft ? "decompose" : "draft" });
|
|
117
|
+
const draftPrompt = buildDraftPrompt(dispatch, { attachmentReferences: stagedAttachments.textReferences });
|
|
118
|
+
const providerPrompt = buildProviderCliPrompt(dispatch, draftPrompt, context.runtimeEnvironment);
|
|
119
|
+
const pipePromptToStdin = shouldPipePromptToStdin(dispatch.execution.provider, providerPrompt);
|
|
69
120
|
const command = buildProviderCliCommand(dispatch, {
|
|
70
121
|
worktreePath: worktree.worktreePath,
|
|
71
|
-
prompt:
|
|
122
|
+
prompt: draftPrompt,
|
|
123
|
+
promptFromStdin: pipePromptToStdin,
|
|
72
124
|
completionTools: completionTools ?? undefined,
|
|
73
|
-
skipGitRepoCheck: false
|
|
125
|
+
skipGitRepoCheck: false,
|
|
126
|
+
imagePaths: stagedAttachments.imagePaths,
|
|
127
|
+
runtimeEnvironment: context.runtimeEnvironment
|
|
74
128
|
});
|
|
75
129
|
const draftArtifactSlugs = dispatch.artifacts
|
|
76
130
|
.filter((artifact) => artifact.scope === "operational" && ["command", "skill", "hook"].includes(artifact.kind))
|
|
@@ -110,7 +164,8 @@ export class ProcessBackedProviderCliService {
|
|
|
110
164
|
command: command.command,
|
|
111
165
|
args: command.args,
|
|
112
166
|
cwd: worktree.worktreePath,
|
|
113
|
-
env: providerProcessEnv(dispatch)
|
|
167
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment),
|
|
168
|
+
stdin: pipePromptToStdin ? providerPrompt : undefined
|
|
114
169
|
}, {
|
|
115
170
|
onStdoutLine: async (line) => {
|
|
116
171
|
for (const event of outputState.consume(line)) {
|
|
@@ -237,7 +292,8 @@ export class ProcessBackedProviderCliService {
|
|
|
237
292
|
: developCycle
|
|
238
293
|
? buildDevelopCyclePrompt(dispatch, phase)
|
|
239
294
|
: buildImplementationPrompt(dispatch, phase),
|
|
240
|
-
completionTools
|
|
295
|
+
completionTools,
|
|
296
|
+
runtimeEnvironment: context.runtimeEnvironment
|
|
241
297
|
});
|
|
242
298
|
await context.emit({
|
|
243
299
|
role: "runner",
|
|
@@ -264,7 +320,7 @@ export class ProcessBackedProviderCliService {
|
|
|
264
320
|
command: command.command,
|
|
265
321
|
args: command.args,
|
|
266
322
|
cwd: worktree.worktreePath,
|
|
267
|
-
env: providerProcessEnv(dispatch)
|
|
323
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
268
324
|
}, {
|
|
269
325
|
onStdoutLine: async (line) => {
|
|
270
326
|
for (const event of outputState.consume(line)) {
|
|
@@ -289,8 +345,8 @@ export class ProcessBackedProviderCliService {
|
|
|
289
345
|
});
|
|
290
346
|
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
291
347
|
const committed = await commitWorktreeChanges(this.git, worktree.worktreePath, dispatch.workItem.id);
|
|
292
|
-
if (developCycle || debugCycle || phase === "implementation") {
|
|
293
|
-
await this.git
|
|
348
|
+
if ((developCycle || debugCycle || phase === "implementation") && providerOwnsBranchPublication(context)) {
|
|
349
|
+
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, context.emit);
|
|
294
350
|
await context.emit({
|
|
295
351
|
role: "runner",
|
|
296
352
|
kind: "checkpoint",
|
|
@@ -352,7 +408,8 @@ export class ProcessBackedProviderCliService {
|
|
|
352
408
|
const command = buildProviderCliCommand(dispatch, {
|
|
353
409
|
worktreePath: worktree.worktreePath,
|
|
354
410
|
prompt: buildArchitectPrompt(dispatch),
|
|
355
|
-
completionTools
|
|
411
|
+
completionTools,
|
|
412
|
+
runtimeEnvironment: context.runtimeEnvironment
|
|
356
413
|
});
|
|
357
414
|
const commandText = formatProviderCliCommandForCheckpoint(command);
|
|
358
415
|
await context.emit({
|
|
@@ -376,7 +433,7 @@ export class ProcessBackedProviderCliService {
|
|
|
376
433
|
command: command.command,
|
|
377
434
|
args: command.args,
|
|
378
435
|
cwd: worktree.worktreePath,
|
|
379
|
-
env: providerProcessEnv(dispatch)
|
|
436
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
380
437
|
}, {
|
|
381
438
|
onStdoutLine: async (line) => {
|
|
382
439
|
for (const event of outputState.consume(line)) {
|
|
@@ -443,7 +500,8 @@ export class ProcessBackedProviderCliService {
|
|
|
443
500
|
const command = buildProviderCliCommand(dispatch, {
|
|
444
501
|
worktreePath: worktree.worktreePath,
|
|
445
502
|
prompt: buildQaPrompt(dispatch, specBody),
|
|
446
|
-
completionTools
|
|
503
|
+
completionTools,
|
|
504
|
+
runtimeEnvironment: context.runtimeEnvironment
|
|
447
505
|
});
|
|
448
506
|
const commandText = formatProviderCliCommandForCheckpoint(command);
|
|
449
507
|
await context.emit({
|
|
@@ -467,7 +525,7 @@ export class ProcessBackedProviderCliService {
|
|
|
467
525
|
command: command.command,
|
|
468
526
|
args: command.args,
|
|
469
527
|
cwd: worktree.worktreePath,
|
|
470
|
-
env: providerProcessEnv(dispatch)
|
|
528
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
471
529
|
}, {
|
|
472
530
|
onStdoutLine: async (line) => {
|
|
473
531
|
for (const event of outputState.consume(line)) {
|
|
@@ -498,7 +556,7 @@ export class ProcessBackedProviderCliService {
|
|
|
498
556
|
? "Provider CLI QA completed but did not produce the complete durable QA artifact bundle."
|
|
499
557
|
: "Provider CLI QA completed but did not call the Forge QA completion tool.");
|
|
500
558
|
}
|
|
501
|
-
enforceQaBrowserLaunchTargetGate(dispatch, qaArtifact);
|
|
559
|
+
await enforceQaBrowserLaunchTargetGate(dispatch, qaArtifact);
|
|
502
560
|
if (!liveQaEvents.hasEmitted()) {
|
|
503
561
|
await emitQaArtifactRecorded(context.emit, worktree.worktreePath, qaArtifact);
|
|
504
562
|
await emitQaStructuredEvents(context.emit, qaArtifact);
|
|
@@ -522,19 +580,29 @@ export class ProcessBackedProviderCliService {
|
|
|
522
580
|
artifacts: [qaArtifact]
|
|
523
581
|
};
|
|
524
582
|
}
|
|
583
|
+
async runCiRepair(context) {
|
|
584
|
+
return await this.runPullRequestRepair(context, "ci_repair");
|
|
585
|
+
}
|
|
525
586
|
async runConflictResolve(context) {
|
|
587
|
+
return await this.runPullRequestRepair(context, "conflict_resolve");
|
|
588
|
+
}
|
|
589
|
+
async runPullRequestRepair(context, route) {
|
|
526
590
|
const { dispatch, worktree } = context;
|
|
527
|
-
const
|
|
591
|
+
const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
|
|
592
|
+
const addressComments = !repairChecks && isPullRequestCommentsDispatch(dispatch);
|
|
528
593
|
const command = buildProviderCliCommand(dispatch, {
|
|
529
594
|
worktreePath: worktree.worktreePath,
|
|
530
|
-
prompt:
|
|
531
|
-
?
|
|
532
|
-
:
|
|
595
|
+
prompt: repairChecks
|
|
596
|
+
? buildPullRequestCheckRepairPrompt(dispatch, worktree.branchName)
|
|
597
|
+
: addressComments
|
|
598
|
+
? buildPullRequestCommentsPrompt(dispatch, worktree.branchName)
|
|
599
|
+
: buildConflictResolvePrompt(dispatch, worktree.branchName),
|
|
600
|
+
runtimeEnvironment: context.runtimeEnvironment
|
|
533
601
|
});
|
|
534
602
|
await context.emit({
|
|
535
603
|
role: "runner",
|
|
536
604
|
kind: "checkpoint",
|
|
537
|
-
content: `Starting provider CLI ${addressComments ? "PR comment address" : "conflict resolve"}: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
605
|
+
content: `Starting provider CLI ${repairChecks ? "CI repair" : addressComments ? "PR comment address" : "conflict resolve"}: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
538
606
|
eventType: "provider_cli_started"
|
|
539
607
|
});
|
|
540
608
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
@@ -542,7 +610,7 @@ export class ProcessBackedProviderCliService {
|
|
|
542
610
|
command: command.command,
|
|
543
611
|
args: command.args,
|
|
544
612
|
cwd: worktree.worktreePath,
|
|
545
|
-
env: providerProcessEnv(dispatch)
|
|
613
|
+
env: providerProcessEnv(dispatch, context.runtimeEnvironment)
|
|
546
614
|
}, {
|
|
547
615
|
onStdoutLine: async (line) => {
|
|
548
616
|
for (const event of outputState.consume(line)) {
|
|
@@ -555,23 +623,94 @@ export class ProcessBackedProviderCliService {
|
|
|
555
623
|
throw new Error(`${command.command} exited ${result.exitCode}${stderr ? `: ${stderr}` : ""}`);
|
|
556
624
|
}
|
|
557
625
|
const committed = await commitWorktreeChanges(this.git, worktree.worktreePath, dispatch.workItem.id);
|
|
558
|
-
|
|
626
|
+
if (providerOwnsBranchPublication(context)) {
|
|
627
|
+
await pushWorktreeBranch(this.git, worktree.worktreePath, worktree.branchName, context.emit);
|
|
628
|
+
}
|
|
559
629
|
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
560
630
|
await context.emit({
|
|
561
631
|
role: "assistant",
|
|
562
632
|
kind: "message",
|
|
563
|
-
content: outputState.finalMessage || (
|
|
564
|
-
? `${dispatch.execution.provider} CLI
|
|
565
|
-
:
|
|
566
|
-
|
|
633
|
+
content: outputState.finalMessage || (repairChecks
|
|
634
|
+
? `${dispatch.execution.provider} CLI repaired pull request checks for ${worktree.branchName}.`
|
|
635
|
+
: addressComments
|
|
636
|
+
? `${dispatch.execution.provider} CLI addressed pull request comments for ${worktree.branchName}.`
|
|
637
|
+
: `${dispatch.execution.provider} CLI resolved merge conflicts for ${worktree.branchName}.`),
|
|
638
|
+
eventType: repairChecks ? "pr_checks_complete" : addressComments ? "pr_comments_complete" : "conflict_resolve_complete"
|
|
567
639
|
});
|
|
568
640
|
return {
|
|
569
|
-
summary:
|
|
570
|
-
? `${dispatch.execution.provider} CLI
|
|
571
|
-
:
|
|
641
|
+
summary: repairChecks
|
|
642
|
+
? `${dispatch.execution.provider} CLI repaired PR checks on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
643
|
+
: addressComments
|
|
644
|
+
? `${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)}).`
|
|
572
646
|
};
|
|
573
647
|
}
|
|
574
648
|
}
|
|
649
|
+
async function stageDraftAttachments(dispatch, metadataRoot) {
|
|
650
|
+
const attachments = dispatch.draft?.attachments ?? [];
|
|
651
|
+
const stageable = attachments.filter((attachment) => (attachment.kind === "text" && typeof attachment.textContent === "string")
|
|
652
|
+
|| (attachment.kind === "image" && typeof attachment.contentBase64 === "string"));
|
|
653
|
+
if (stageable.length === 0) {
|
|
654
|
+
return { textReferences: [], imagePaths: [] };
|
|
655
|
+
}
|
|
656
|
+
const attachmentRoot = path.join(metadataRoot, "draft-attachments");
|
|
657
|
+
const worktreeRoot = path.resolve(metadataRoot, "../../..");
|
|
658
|
+
await fs.mkdir(attachmentRoot, { recursive: true });
|
|
659
|
+
const textReferences = [];
|
|
660
|
+
const imagePaths = [];
|
|
661
|
+
for (const attachment of stageable) {
|
|
662
|
+
const fileName = `${String(attachment.index + 1).padStart(2, "0")}-${safeAttachmentFileName(attachment.fileName)}`;
|
|
663
|
+
const absolutePath = path.join(attachmentRoot, fileName);
|
|
664
|
+
if (attachment.kind === "text") {
|
|
665
|
+
await fs.writeFile(absolutePath, attachment.textContent ?? "", { encoding: "utf8", mode: 0o600 });
|
|
666
|
+
textReferences.push({ index: attachment.index, path: path.relative(worktreeRoot, absolutePath).replaceAll(path.sep, "/") });
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
const imageContent = decodeDraftImageAttachment(attachment.contentBase64 ?? "", attachment.fileName, attachment.index);
|
|
670
|
+
await fs.writeFile(absolutePath, imageContent, { mode: 0o600 });
|
|
671
|
+
imagePaths.push(absolutePath);
|
|
672
|
+
}
|
|
673
|
+
return { textReferences, imagePaths };
|
|
674
|
+
}
|
|
675
|
+
function decodeDraftImageAttachment(contentBase64, fileName, index) {
|
|
676
|
+
const normalized = contentBase64.replace(/\s+/gu, "");
|
|
677
|
+
if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(normalized)) {
|
|
678
|
+
throw new Error(`Draft image attachment ${index + 1} (${fileName}) is not valid base64.`);
|
|
679
|
+
}
|
|
680
|
+
const decoded = Buffer.from(normalized, "base64");
|
|
681
|
+
if (decoded.length === 0 || decoded.toString("base64") !== normalized) {
|
|
682
|
+
throw new Error(`Draft image attachment ${index + 1} (${fileName}) is not valid base64.`);
|
|
683
|
+
}
|
|
684
|
+
return decoded;
|
|
685
|
+
}
|
|
686
|
+
function safeAttachmentFileName(fileName) {
|
|
687
|
+
const sanitized = path.basename(fileName || "attachment.txt")
|
|
688
|
+
.replace(/[^a-zA-Z0-9._-]/gu, "_")
|
|
689
|
+
.replace(/[. ]+$/gu, "") || "attachment.txt";
|
|
690
|
+
const maxFileNameLength = 120;
|
|
691
|
+
const clipped = clipFileName(sanitized, maxFileNameLength);
|
|
692
|
+
const extension = path.extname(clipped);
|
|
693
|
+
const stem = path.basename(clipped, extension).replace(/[. ]+$/gu, "") || "attachment";
|
|
694
|
+
const windowsReservedName = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/iu;
|
|
695
|
+
const safeStem = stem === "." || stem === ".." || windowsReservedName.test(stem) ? `attachment-${stem.replaceAll(".", "_")}` : stem;
|
|
696
|
+
return clipFileName(`${safeStem}${extension}`, maxFileNameLength);
|
|
697
|
+
}
|
|
698
|
+
function clipFileName(fileName, maxLength) {
|
|
699
|
+
if (fileName.length <= maxLength) {
|
|
700
|
+
return fileName;
|
|
701
|
+
}
|
|
702
|
+
const originalExtension = path.extname(fileName);
|
|
703
|
+
const extension = originalExtension.length > maxLength - 1
|
|
704
|
+
? originalExtension.slice(0, maxLength - 1)
|
|
705
|
+
: originalExtension;
|
|
706
|
+
const stem = path.basename(fileName, originalExtension);
|
|
707
|
+
const maxStemLength = Math.max(1, maxLength - extension.length);
|
|
708
|
+
return `${stem.slice(0, maxStemLength)}${extension}`;
|
|
709
|
+
}
|
|
710
|
+
const providerPromptStdinThreshold = 8_000;
|
|
711
|
+
function shouldPipePromptToStdin(provider, prompt) {
|
|
712
|
+
return provider.trim().toLowerCase() === "codex" && Buffer.byteLength(prompt, "utf8") > providerPromptStdinThreshold;
|
|
713
|
+
}
|
|
575
714
|
function isFakeWorkerModel(model) {
|
|
576
715
|
return model.trim().toLowerCase() === fakeWorkerModel;
|
|
577
716
|
}
|
|
@@ -3,12 +3,14 @@ import type { WorktreeHandle } from "../../worktree/worktree.types.js";
|
|
|
3
3
|
export interface ProviderCliRunContext {
|
|
4
4
|
readonly dispatch: RunOnceDispatchPacket;
|
|
5
5
|
readonly worktree: WorktreeHandle;
|
|
6
|
+
readonly runtimeEnvironment?: Readonly<Record<string, string>>;
|
|
6
7
|
readonly signal?: AbortSignal;
|
|
7
8
|
emit(event: RunnerDispatchEvent): Promise<void>;
|
|
8
9
|
}
|
|
9
10
|
export interface ProviderCliDraftContext {
|
|
10
11
|
readonly dispatch: RunOnceDispatchPacket;
|
|
11
12
|
readonly worktree: WorktreeHandle;
|
|
13
|
+
readonly runtimeEnvironment?: Readonly<Record<string, string>>;
|
|
12
14
|
readonly signal?: AbortSignal;
|
|
13
15
|
emit(event: RunnerDispatchEvent): Promise<void>;
|
|
14
16
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RunOnceResultPacket, RunnerDispatchEvent } from "./session.types.js";
|
|
2
|
+
export declare function redactRunnerDispatchEvent(event: RunnerDispatchEvent, exactSecrets?: readonly string[]): RunnerDispatchEvent;
|
|
3
|
+
export declare function redactRunOnceResultPacket(result: RunOnceResultPacket, exactSecrets?: readonly string[]): RunOnceResultPacket;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { redactSecrets } from "../shared/redaction/redaction.js";
|
|
2
|
+
const integrityProtectedEventDataTypes = new Set(["sandbox_started"]);
|
|
3
|
+
export function redactRunnerDispatchEvent(event, exactSecrets = []) {
|
|
4
|
+
if (!event.data || !integrityProtectedEventDataTypes.has(event.eventType)) {
|
|
5
|
+
return redactSecrets(event, exactSecrets);
|
|
6
|
+
}
|
|
7
|
+
// sandbox_started data is runner-authored protocol metadata. Its runtime
|
|
8
|
+
// lease repeats the dispatch fence so the API can validate ownership. The
|
|
9
|
+
// transcript redactor treats any serialized `*Token` field as a secret and
|
|
10
|
+
// must not rewrite that identity before transport or result persistence.
|
|
11
|
+
const { data, ...transcript } = event;
|
|
12
|
+
return {
|
|
13
|
+
...redactSecrets(transcript, exactSecrets),
|
|
14
|
+
data,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function redactRunOnceResultPacket(result, exactSecrets = []) {
|
|
18
|
+
const { events, ...payload } = result;
|
|
19
|
+
return {
|
|
20
|
+
...redactSecrets(payload, exactSecrets),
|
|
21
|
+
events: events.map(event => redactRunnerDispatchEvent(event, exactSecrets)),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { RunOnceDispatchPacket, RunOnceResultPacket, RunnerDispatchEvent } from "./session.types.js";
|
|
2
|
+
import type { HostRuntimeCapacitySnapshot, HostRuntimeReapOptions, HostRuntimeReapReport } from "../runtime/host-runtime-lease.js";
|
|
2
3
|
export interface SessionEventSink {
|
|
3
4
|
emit(event: RunnerDispatchEvent): Promise<void>;
|
|
4
5
|
}
|
|
5
6
|
export interface SessionService {
|
|
6
7
|
runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
|
|
8
|
+
updateLease?(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
|
|
9
|
+
reapHostRuntime?(options: HostRuntimeReapOptions): Promise<HostRuntimeReapReport | null>;
|
|
10
|
+
runtimeCapacity?(): HostRuntimeCapacitySnapshot | null;
|
|
7
11
|
}
|
|
@@ -2,9 +2,14 @@ import type { ProviderCliService } from "./provider-cli/provider-cli.base.js";
|
|
|
2
2
|
import type { SessionEventSink, SessionService } from "./session.base.js";
|
|
3
3
|
import type { RunOnceDispatchPacket, RunOnceResultPacket } from "./session.types.js";
|
|
4
4
|
import type { WorktreeService } from "../worktree/worktree.base.js";
|
|
5
|
+
import { type HostRuntimeCapacitySnapshot, type HostRuntimeLeaseRuntime, type HostRuntimeReapOptions, type HostRuntimeReapReport } from "../runtime/host-runtime-lease.js";
|
|
5
6
|
export declare class BridgeSessionService implements SessionService {
|
|
6
7
|
private readonly worktrees;
|
|
7
8
|
private readonly providerCli;
|
|
8
|
-
|
|
9
|
+
private readonly hostRuntime?;
|
|
10
|
+
constructor(worktrees?: WorktreeService, providerCli?: ProviderCliService, hostRuntime?: HostRuntimeLeaseRuntime | undefined);
|
|
11
|
+
updateLease(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
|
|
12
|
+
reapHostRuntime(options: HostRuntimeReapOptions): Promise<HostRuntimeReapReport | null>;
|
|
13
|
+
runtimeCapacity(): HostRuntimeCapacitySnapshot | null;
|
|
9
14
|
runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
|
|
10
15
|
}
|