@loucompanion/forge-bridge 0.1.3-dev.b6be5e7f9668 → 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.
Files changed (141) hide show
  1. package/README.md +78 -0
  2. package/dist/bridge/bridge.base.d.ts +5 -1
  3. package/dist/bridge/bridge.service.d.ts +52 -3
  4. package/dist/bridge/bridge.service.js +796 -22
  5. package/dist/bridge/bridge.types.d.ts +135 -1
  6. package/dist/cleanup/cleanup.service.d.ts +13 -2
  7. package/dist/cleanup/cleanup.service.js +435 -3
  8. package/dist/cleanup/cleanup.types.d.ts +10 -0
  9. package/dist/cleanup/index.d.ts +1 -1
  10. package/dist/cleanup/index.js +1 -1
  11. package/dist/cleanup/internals.d.ts +2 -2
  12. package/dist/cleanup/internals.js +1 -1
  13. package/dist/cli/cli.help.d.ts +2 -0
  14. package/dist/cli/cli.help.js +217 -0
  15. package/dist/cli/cli.service.d.ts +42 -2
  16. package/dist/cli/cli.service.js +356 -205
  17. package/dist/cli/local-config.d.ts +6 -0
  18. package/dist/cli/local-config.js +45 -2
  19. package/dist/runtime/attempt-node-runtime-launcher.d.ts +15 -0
  20. package/dist/runtime/attempt-node-runtime-launcher.js +50 -0
  21. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  22. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  23. package/dist/runtime/attempt-process-supervisor.d.ts +74 -0
  24. package/dist/runtime/attempt-process-supervisor.js +290 -0
  25. package/dist/runtime/host-listener-inspector.d.ts +28 -0
  26. package/dist/runtime/host-listener-inspector.js +185 -0
  27. package/dist/runtime/host-port-broker.d.ts +76 -0
  28. package/dist/runtime/host-port-broker.js +333 -0
  29. package/dist/runtime/host-runtime-contract.d.ts +39 -0
  30. package/dist/runtime/host-runtime-contract.js +295 -0
  31. package/dist/runtime/host-runtime-lease.d.ts +122 -0
  32. package/dist/runtime/host-runtime-lease.js +1241 -0
  33. package/dist/runtime/host-runtime-promotion.d.ts +21 -0
  34. package/dist/runtime/host-runtime-promotion.js +128 -0
  35. package/dist/runtime/node-host-runtime-adapter.d.ts +44 -0
  36. package/dist/runtime/node-host-runtime-adapter.js +205 -0
  37. package/dist/runtime/playwright-host-runtime-adapter.d.ts +20 -0
  38. package/dist/runtime/playwright-host-runtime-adapter.js +136 -0
  39. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  40. package/dist/runtime/runtime-isolation-lease.js +158 -0
  41. package/dist/sandbox/command-runner.d.ts +4 -0
  42. package/dist/sandbox/command-runner.js +47 -0
  43. package/dist/sandbox/contract.d.ts +5 -0
  44. package/dist/sandbox/contract.js +276 -0
  45. package/dist/sandbox/event-budget.d.ts +11 -0
  46. package/dist/sandbox/event-budget.js +27 -0
  47. package/dist/sandbox/image-policy.d.ts +35 -0
  48. package/dist/sandbox/image-policy.js +247 -0
  49. package/dist/sandbox/index.d.ts +5 -0
  50. package/dist/sandbox/index.js +5 -0
  51. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  52. package/dist/sandbox/lease-watchdog.js +67 -0
  53. package/dist/sandbox/preflight.d.ts +45 -0
  54. package/dist/sandbox/preflight.js +300 -0
  55. package/dist/sandbox/process.d.ts +20 -0
  56. package/dist/sandbox/process.js +209 -0
  57. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  58. package/dist/sandbox/provider-auth/codex.js +212 -0
  59. package/dist/sandbox/repository-lock.d.ts +5 -0
  60. package/dist/sandbox/repository-lock.js +119 -0
  61. package/dist/sandbox/runtime.d.ts +297 -0
  62. package/dist/sandbox/runtime.js +2338 -0
  63. package/dist/sandbox/sandbox.types.d.ts +86 -0
  64. package/dist/sandbox/sandbox.types.js +1 -0
  65. package/dist/sandbox/smoke.d.ts +76 -0
  66. package/dist/sandbox/smoke.js +380 -0
  67. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  68. package/dist/sandbox/worker-supervisor.js +36 -0
  69. package/dist/sandbox/worker.d.ts +1 -0
  70. package/dist/sandbox/worker.js +225 -0
  71. package/dist/sandbox/workspace.service.d.ts +13 -0
  72. package/dist/sandbox/workspace.service.js +67 -0
  73. package/dist/session/phase-completion.artifacts.d.ts +10 -0
  74. package/dist/session/phase-completion.artifacts.js +620 -0
  75. package/dist/session/phase-completion.d.ts +32 -44
  76. package/dist/session/phase-completion.helpers.d.ts +112 -0
  77. package/dist/session/phase-completion.helpers.js +665 -0
  78. package/dist/session/phase-completion.js +28 -3485
  79. package/dist/session/phase-completion.prompts.d.ts +14 -0
  80. package/dist/session/phase-completion.prompts.js +220 -0
  81. package/dist/session/phase-completion.runtime.d.ts +36 -0
  82. package/dist/session/phase-completion.runtime.js +611 -0
  83. package/dist/session/phase-completion.schemas.d.ts +3 -0
  84. package/dist/session/phase-completion.schemas.js +707 -0
  85. package/dist/session/phase-completion.validation.d.ts +34 -0
  86. package/dist/session/phase-completion.validation.js +724 -0
  87. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +93 -0
  88. package/dist/session/provider-cli/provider-cli.artifacts.js +778 -0
  89. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  90. package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
  91. package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
  92. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +14 -0
  93. package/dist/session/provider-cli/provider-cli.deterministic.js +329 -0
  94. package/dist/session/provider-cli/provider-cli.process.d.ts +34 -0
  95. package/dist/session/provider-cli/provider-cli.process.js +316 -0
  96. package/dist/session/provider-cli/provider-cli.prompts.d.ts +47 -0
  97. package/dist/session/provider-cli/provider-cli.prompts.js +573 -0
  98. package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
  99. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +65 -0
  100. package/dist/session/provider-cli/provider-cli.runtime.d.ts +9 -0
  101. package/dist/session/provider-cli/provider-cli.runtime.js +41 -0
  102. package/dist/session/provider-cli/provider-cli.service.d.ts +9 -53
  103. package/dist/session/provider-cli/provider-cli.service.js +182 -2536
  104. package/dist/session/provider-cli/provider-cli.types.d.ts +3 -0
  105. package/dist/session/provider-cli/provider-output-state.d.ts +20 -0
  106. package/dist/session/provider-cli/provider-output-state.js +445 -0
  107. package/dist/session/session-redaction.d.ts +3 -0
  108. package/dist/session/session-redaction.js +23 -0
  109. package/dist/session/session.base.d.ts +4 -0
  110. package/dist/session/session.service.d.ts +6 -1
  111. package/dist/session/session.service.js +202 -53
  112. package/dist/session/session.types.d.ts +123 -1
  113. package/dist/session/session.types.js +301 -3
  114. package/dist/shared/path/path.js +3 -1
  115. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  116. package/dist/shared/process/host-process-recovery.js +78 -0
  117. package/dist/shared/process/host-process-supervisor.d.ts +40 -0
  118. package/dist/shared/process/host-process-supervisor.js +195 -0
  119. package/dist/shared/process/windows-launch.d.ts +8 -0
  120. package/dist/shared/process/windows-launch.js +41 -0
  121. package/dist/shared/provider-capabilities.d.ts +1 -0
  122. package/dist/shared/provider-capabilities.js +3 -0
  123. package/dist/shared/redaction/redaction.js +10 -3
  124. package/dist/version.d.ts +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist/worktree/artifact-injector.js +39 -6
  127. package/dist/worktree/dependency-warmer.d.ts +11 -0
  128. package/dist/worktree/dependency-warmer.js +45 -1
  129. package/dist/worktree/index.d.ts +1 -0
  130. package/dist/worktree/index.js +1 -0
  131. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  132. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  133. package/dist/worktree/worktree-paths.d.ts +4 -0
  134. package/dist/worktree/worktree-paths.js +30 -0
  135. package/dist/worktree/worktree.base.d.ts +3 -0
  136. package/dist/worktree/worktree.service.d.ts +8 -3
  137. package/dist/worktree/worktree.service.js +89 -52
  138. package/dist/worktree/worktree.types.d.ts +3 -1
  139. package/package.json +6 -3
  140. package/runtime/windows-job-supervisor.ps1 +287 -0
  141. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -1,12 +1,27 @@
1
- import { redactSecrets } from "../shared/redaction/redaction.js";
1
+ import { redactRunnerDispatchEvent, redactRunOnceResultPacket } from "./session-redaction.js";
2
2
  import { RoutingProviderCliService } from "./provider-cli/provider-cli.service.js";
3
3
  import { GitWorktreeService } from "../worktree/worktree.service.js";
4
+ import { SandboxWorkspaceService } from "../sandbox/workspace.service.js";
5
+ import { HostRuntimeAdmissionExpiredError, hostRuntimeEventResources, } from "../runtime/host-runtime-lease.js";
4
6
  export class BridgeSessionService {
5
7
  worktrees;
6
8
  providerCli;
7
- constructor(worktrees = new GitWorktreeService(), providerCli = new RoutingProviderCliService()) {
9
+ hostRuntime;
10
+ constructor(worktrees = process.env.FORGE_SANDBOX_ID
11
+ ? new SandboxWorkspaceService()
12
+ : new GitWorktreeService(), providerCli = new RoutingProviderCliService(), hostRuntime) {
8
13
  this.worktrees = worktrees;
9
14
  this.providerCli = providerCli;
15
+ this.hostRuntime = hostRuntime;
16
+ }
17
+ async updateLease(dispatchAttemptId, fencingToken, expiresAt) {
18
+ await this.hostRuntime?.updateLease(dispatchAttemptId, fencingToken, expiresAt);
19
+ }
20
+ async reapHostRuntime(options) {
21
+ return await this.hostRuntime?.reap?.(options) ?? null;
22
+ }
23
+ runtimeCapacity() {
24
+ return this.hostRuntime?.snapshot?.() ?? null;
10
25
  }
11
26
  async runOnce(dispatch, sink, signal) {
12
27
  validateApiKeySecretGrant(dispatch);
@@ -16,7 +31,12 @@ export class BridgeSessionService {
16
31
  if (signal?.aborted) {
17
32
  return;
18
33
  }
19
- const redacted = redactSecrets(event, redactionSecrets);
34
+ const redacted = redactRunnerDispatchEvent(event, redactionSecrets);
35
+ events.push(redacted);
36
+ await sink.emit(redacted);
37
+ };
38
+ const emitLifecycle = async (event) => {
39
+ const redacted = redactRunnerDispatchEvent(event, redactionSecrets);
20
40
  events.push(redacted);
21
41
  await sink.emit(redacted);
22
42
  };
@@ -27,59 +47,142 @@ export class BridgeSessionService {
27
47
  content: "forge-bridge accepted run-once dispatch.",
28
48
  eventType: "dispatch_accepted"
29
49
  });
30
- if (dispatch.draft) {
31
- const run = await this.providerCli.runDraft({
32
- dispatch,
33
- emit,
34
- signal
50
+ let worktree = null;
51
+ let hostSingletonKeys = null;
52
+ let hostLease = null;
53
+ try {
54
+ if (dispatch.sandbox?.executionMode === "host" && this.hostRuntime) {
55
+ await emitLifecycle({
56
+ role: "runner",
57
+ kind: "checkpoint",
58
+ content: "Evaluating runner-local managed host admission before preparing the worktree.",
59
+ eventType: "host_runtime_admission_waiting",
60
+ });
61
+ try {
62
+ hostSingletonKeys = await this.hostRuntime.acquire(dispatch, signal);
63
+ }
64
+ catch (error) {
65
+ if (error instanceof HostRuntimeAdmissionExpiredError) {
66
+ await emitLifecycle({
67
+ role: "runner",
68
+ kind: "error",
69
+ content: "Managed host admission expired while the project singleton remained busy; no worktree or runtime resources were created.",
70
+ eventType: "host_runtime_admission_expired",
71
+ }).catch(() => undefined);
72
+ }
73
+ throw error;
74
+ }
75
+ throwIfAborted(signal);
76
+ await emitLifecycle({
77
+ role: "runner",
78
+ kind: "checkpoint",
79
+ content: hostSingletonKeys.length > 0
80
+ ? `Acquired managed host singleton ${hostSingletonKeys.join(", ")}.`
81
+ : "Observed assigned-port evidence permits provisional parallel host admission; the prepared runtime contract must still match before launch.",
82
+ eventType: "host_runtime_admission_acquired",
83
+ });
84
+ }
85
+ worktree = await this.worktrees.prepare({
86
+ repo: dispatch.repo,
87
+ workItem: dispatch.workItem,
88
+ baseBranch: dispatch.cycleRun.baseBranch || dispatch.cycleRun.requestedBase,
89
+ worktreeKey: dispatch.lease.worktreeKey,
90
+ ownerType: dispatch.cycleRun.ownerType ?? dispatch.lease.ownerType ?? null,
91
+ ownerId: dispatch.cycleRun.ownerId ?? dispatch.lease.ownerId ?? null,
92
+ lane: dispatch.execution.lane,
93
+ phase: dispatch.cycleRun.phase,
94
+ allowedRepoRoot: dispatch.serverLocal.repoRoot,
95
+ cleanupWorktrees: dispatch.serverLocal.cleanupWorktrees,
96
+ artifacts: dispatch.artifacts,
97
+ priorPhaseArtifacts: dispatch.priorPhaseArtifacts,
98
+ qaRework: dispatch.qaRework,
99
+ warmPathOverrides: dispatch.warmPathOverrides
35
100
  });
101
+ const providerDispatch = withPreparedQaReworkPacket(dispatch, worktree.qaReworkPacketPath);
36
102
  throwIfAborted(signal);
37
- return redactSecrets({
38
- schemaVersion: 1,
39
- exitCode: 0,
40
- summary: run.summary,
41
- events,
42
- spec: run.spec ?? null,
43
- artifact: run.artifact ?? null,
44
- artifacts: run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : []
45
- }, redactionSecrets);
46
- }
47
- const worktree = await this.worktrees.prepare({
48
- repo: dispatch.repo,
49
- workItem: dispatch.workItem,
50
- baseBranch: dispatch.cycleRun.baseBranch || dispatch.cycleRun.requestedBase,
51
- worktreeKey: dispatch.lease.worktreeKey,
52
- ownerType: dispatch.cycleRun.ownerType ?? dispatch.lease.ownerType ?? null,
53
- ownerId: dispatch.cycleRun.ownerId ?? dispatch.lease.ownerId ?? null,
54
- lane: dispatch.execution.lane,
55
- phase: dispatch.cycleRun.phase,
56
- allowedRepoRoot: dispatch.serverLocal.repoRoot,
57
- cleanupWorktrees: dispatch.serverLocal.cleanupWorktrees,
58
- artifacts: dispatch.artifacts,
59
- qaRework: dispatch.qaRework,
60
- warmPathOverrides: dispatch.warmPathOverrides
61
- });
62
- const providerDispatch = withPreparedQaReworkPacket(dispatch, worktree.qaReworkPacketPath);
63
- throwIfAborted(signal);
64
- await emit({
65
- role: "runner",
66
- kind: "checkpoint",
67
- content: `Prepared worktree ${worktree.branchName}.`,
68
- eventType: "worktree_prepared"
69
- });
70
- if (worktree.dependencyWarm) {
71
- await emitDependencyWarmEvents(worktree.dependencyWarm, emit);
72
- }
73
- try {
103
+ await emit({
104
+ role: "runner",
105
+ kind: "checkpoint",
106
+ content: `Prepared worktree ${worktree.branchName}.`,
107
+ eventType: "worktree_prepared"
108
+ });
109
+ if ((worktree.hydratedPriorPhaseArtifactCount ?? 0) > 0) {
110
+ await emit({
111
+ role: "runner",
112
+ kind: "checkpoint",
113
+ content: `Hydrated ${worktree.hydratedPriorPhaseArtifactCount} persisted prior-phase artifact(s) into the prepared worktree.`,
114
+ eventType: "prior_phase_artifacts_hydrated"
115
+ });
116
+ }
117
+ if (worktree.dependencyWarm) {
118
+ await emitDependencyWarmEvents(worktree.dependencyWarm, emit);
119
+ }
120
+ if (dispatch.sandbox?.executionMode === "host" && this.hostRuntime) {
121
+ hostLease = await this.hostRuntime.start(dispatch, worktree.worktreePath, signal);
122
+ await emitLifecycle({
123
+ role: "runner",
124
+ kind: "checkpoint",
125
+ content: `Host runtime lease ${hostLease.lease.leaseId} is active for this worktree.`,
126
+ eventType: "sandbox_started",
127
+ occurredAt: hostLease.lease.stateChangedAt,
128
+ data: JSON.stringify(hostRuntimeEventResources(hostLease.lease, hostLease.runtimeContract)),
129
+ });
130
+ await emitLifecycle({
131
+ role: "runner",
132
+ kind: "checkpoint",
133
+ content: "Managed host fallback isolates the worktree, attempt paths, assigned loopback ports, and supervised process trees, but not the host filesystem, network, credentials, or kernel. Serialized projects also hold a runner-local singleton. Enable Run in Docker in Project Settings when stronger isolation is required.",
134
+ eventType: "host_runtime_isolation_notice",
135
+ data: JSON.stringify({
136
+ isolationStrength: "managed_host_process",
137
+ remediation: "Enable Run in Docker in Project Settings.",
138
+ }),
139
+ });
140
+ const unconfiguredPortCount = hostLease.lease.resources.ports.length;
141
+ hostLease = await this.hostRuntime.startConfiguredProject(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken, signal);
142
+ if (hostLease.lease.resources.ports.length > unconfiguredPortCount) {
143
+ const assigned = hostLease.lease.resources.ports.at(-1);
144
+ await emitLifecycle({
145
+ role: "runner",
146
+ kind: "checkpoint",
147
+ content: `Verified supervised project runtime ${assigned.name} on ${assigned.hostIp}:${assigned.hostPort}.`,
148
+ eventType: "host_runtime_project_ready",
149
+ data: JSON.stringify(hostRuntimeEventResources(hostLease.lease, hostLease.runtimeContract)),
150
+ });
151
+ }
152
+ }
153
+ if (dispatch.draft) {
154
+ throwIfAborted(signal);
155
+ const run = await this.providerCli.runDraft({
156
+ dispatch: providerDispatch,
157
+ worktree,
158
+ runtimeEnvironment: hostLease?.environment,
159
+ emit,
160
+ signal
161
+ });
162
+ throwIfAborted(signal);
163
+ const draftArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
164
+ return redactRunOnceResultPacket({
165
+ schemaVersion: 1,
166
+ exitCode: 0,
167
+ summary: run.summary,
168
+ events,
169
+ spec: run.spec ?? null,
170
+ artifact: run.artifact ?? null,
171
+ artifacts: draftArtifacts
172
+ }, redactionSecrets);
173
+ }
74
174
  throwIfAborted(signal);
75
- const lane = dispatch.execution.lane.toLowerCase();
76
- const role = dispatch.execution.role.toLowerCase();
175
+ const lane = dispatch.execution.lane.trim().toLowerCase();
176
+ const role = dispatch.execution.role.trim().toLowerCase();
177
+ const cycleKind = dispatch.cycleRun.kind.trim().toLowerCase();
77
178
  const isQa = role === "qa" || lane === "qa";
78
- const isArchitect = role === "architect" || lane === "architect" || dispatch.cycleRun.kind.toLowerCase() === "architect";
179
+ const isArchitect = role === "architect" || lane === "architect" || cycleKind === "architect";
180
+ const isCiRepair = role === "ci_repair" || lane === "ci_repair" || cycleKind === "pr_checks";
79
181
  const isConflictResolve = role === "conflict_resolve" || lane === "conflict_resolve";
80
182
  const context = {
81
183
  dispatch: providerDispatch,
82
184
  worktree,
185
+ runtimeEnvironment: hostLease?.environment,
83
186
  emit,
84
187
  signal
85
188
  };
@@ -87,12 +190,14 @@ export class BridgeSessionService {
87
190
  ? await this.providerCli.runQa(context)
88
191
  : isArchitect
89
192
  ? await this.providerCli.runArchitect(context)
90
- : isConflictResolve
91
- ? await this.providerCli.runConflictResolve(context)
92
- : await this.providerCli.runImplementation(context);
193
+ : isCiRepair
194
+ ? await this.providerCli.runCiRepair(context)
195
+ : isConflictResolve
196
+ ? await this.providerCli.runConflictResolve(context)
197
+ : await this.providerCli.runImplementation(context);
93
198
  throwIfAborted(signal);
94
199
  const phaseArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
95
- return redactSecrets({
200
+ return redactRunOnceResultPacket({
96
201
  schemaVersion: 1,
97
202
  exitCode: 0,
98
203
  summary: run.summary,
@@ -103,7 +208,51 @@ export class BridgeSessionService {
103
208
  }, redactionSecrets);
104
209
  }
105
210
  finally {
106
- await this.worktrees.release(worktree);
211
+ if (hostLease && dispatch.sandbox) {
212
+ const teardownAt = new Date().toISOString();
213
+ await emitLifecycle({
214
+ role: "runner",
215
+ kind: "checkpoint",
216
+ content: `Releasing host runtime lease ${hostLease.lease.leaseId}.`,
217
+ eventType: "sandbox_teardown_started",
218
+ occurredAt: teardownAt,
219
+ }).catch(() => undefined);
220
+ try {
221
+ const released = await this.hostRuntime?.release(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken);
222
+ if (!released) {
223
+ await emitLifecycle({
224
+ role: "runner",
225
+ kind: "error",
226
+ content: "Host runtime cleanup requires reconciliation: the runtime lease was not current during release.",
227
+ eventType: "teardown_failed",
228
+ occurredAt: new Date().toISOString(),
229
+ }).catch(() => undefined);
230
+ }
231
+ else {
232
+ await emitLifecycle({
233
+ role: "runner",
234
+ kind: "checkpoint",
235
+ content: `Released host runtime lease ${released.leaseId}.`,
236
+ eventType: "sandbox_teardown_completed",
237
+ occurredAt: released.releasedAt,
238
+ }).catch(() => undefined);
239
+ }
240
+ }
241
+ catch (error) {
242
+ await emitLifecycle({
243
+ role: "runner",
244
+ kind: "error",
245
+ content: `Host runtime cleanup requires reconciliation: ${error instanceof Error ? error.message : String(error)}`,
246
+ eventType: "teardown_failed",
247
+ occurredAt: new Date().toISOString(),
248
+ }).catch(() => undefined);
249
+ }
250
+ }
251
+ if (!hostLease && hostSingletonKeys && dispatch.sandbox) {
252
+ await this.hostRuntime?.release(dispatch.sandbox.dispatchAttemptId, dispatch.lease.fencingToken).catch(() => undefined);
253
+ }
254
+ if (worktree)
255
+ await this.worktrees.release(worktree);
107
256
  }
108
257
  }
109
258
  }
@@ -1,5 +1,5 @@
1
1
  export interface RunOnceDispatchPacket {
2
- readonly schemaVersion: 1;
2
+ readonly schemaVersion: 1 | 2;
3
3
  readonly userId: string;
4
4
  readonly workItem: DispatchWorkItem;
5
5
  readonly repo: DispatchRepo;
@@ -8,24 +8,76 @@ export interface RunOnceDispatchPacket {
8
8
  readonly lease: DispatchLease;
9
9
  readonly execution: DispatchExecution;
10
10
  readonly serverLocal: DispatchServerLocalConfig;
11
+ readonly sandbox?: DispatchSandboxContext | null;
11
12
  readonly pullRequest?: DispatchPullRequestContext | null;
12
13
  readonly apiKeySecretGrant?: ApiKeySecretGrant | null;
14
+ readonly apiKeySecretGrantHandle?: ApiKeySecretGrantHandle | null;
13
15
  readonly artifacts: readonly DispatchResolvedArtifact[];
16
+ readonly priorPhaseArtifacts?: readonly DispatchPriorPhaseArtifact[];
14
17
  readonly warmPathOverrides?: readonly string[];
15
18
  readonly qaBrowser?: DispatchQaBrowserContext | null;
16
19
  readonly qaScope?: DispatchQaScope | null;
17
20
  readonly qaRework?: DispatchQaReworkContext | null;
18
21
  readonly architectRevision?: DispatchArchitectRevisionContext | null;
22
+ readonly architectDependencies?: DispatchArchitectDependencyContext | null;
19
23
  readonly storyDevelop?: DispatchStoryDevelopContext | null;
20
24
  readonly storyQa?: DispatchStoryQaContext | null;
25
+ readonly designerRefs?: DispatchDesignerRefHandoffContext | null;
21
26
  readonly draft?: DispatchDraftPrompt | null;
22
27
  }
28
+ export interface DispatchSandboxContext {
29
+ readonly dispatchAttemptId: string;
30
+ readonly executionLeaseId: string;
31
+ readonly runnerInstanceId: string;
32
+ readonly executionMode: "host" | "docker";
33
+ readonly executionPolicy: "host_allowed" | "sandbox_preferred" | "sandbox_required";
34
+ readonly networkPolicy: DispatchSandboxNetworkPolicy;
35
+ readonly requestedResources: DispatchSandboxResourceRequest;
36
+ readonly runtime?: string | null;
37
+ readonly sandboxConfigCommitSha?: string | null;
38
+ readonly inputCommitSha?: string | null;
39
+ readonly contractDigest?: string | null;
40
+ readonly imageDigest?: string | null;
41
+ readonly publicationMode: "bridge_push" | "shared_filesystem";
42
+ readonly publishGrantHandle?: DispatchPublishGrantHandle | null;
43
+ }
44
+ export interface DispatchPublishGrantHandle {
45
+ readonly grantId: string;
46
+ readonly branch: string;
47
+ readonly expiresAt: string;
48
+ }
49
+ export interface DispatchSandboxResourceRequest {
50
+ readonly sandboxSlots: number;
51
+ readonly cpuMillis: number;
52
+ readonly workerMemoryLimitBytes: number;
53
+ readonly serviceMemoryLimitBytes: number;
54
+ readonly runtimeOverheadBytes: number;
55
+ readonly memoryBytes: number;
56
+ readonly workspaceBytes: number;
57
+ readonly pids: number;
58
+ readonly buildSlots: number;
59
+ }
60
+ export interface DispatchSandboxNetworkPolicy {
61
+ readonly mode: "offline" | "public_allowlist" | "open_public";
62
+ readonly allowedHosts: readonly string[];
63
+ }
23
64
  export interface DispatchDraftPrompt {
24
65
  readonly kind: string;
25
66
  readonly systemPrompt: string;
26
67
  readonly userPrompt: string;
27
68
  readonly projectName?: string | null;
28
69
  readonly repoName?: string | null;
70
+ readonly attachments?: readonly DispatchDraftAttachment[];
71
+ }
72
+ export interface DispatchDraftAttachment {
73
+ readonly index: number;
74
+ readonly fileName: string;
75
+ readonly contentType: string;
76
+ readonly kind: string;
77
+ readonly byteCount: number;
78
+ readonly summary: string;
79
+ readonly textContent?: string | null;
80
+ readonly contentBase64?: string | null;
29
81
  }
30
82
  export interface DispatchQaBrowserContext {
31
83
  readonly required: boolean;
@@ -69,6 +121,22 @@ export interface DispatchArchitectRevisionContext {
69
121
  readonly priorContentHash: string;
70
122
  readonly feedback: string;
71
123
  }
124
+ export interface DispatchArchitectDependencyContext {
125
+ readonly baseBranch: string;
126
+ readonly prerequisites: readonly DispatchArchitectDependencyItemContext[];
127
+ }
128
+ export interface DispatchArchitectDependencyItemContext {
129
+ readonly storyId: string;
130
+ readonly title: string;
131
+ readonly scheduleIndex?: number | null;
132
+ readonly waveIndex?: number | null;
133
+ readonly featureScheduleIndex?: number | null;
134
+ readonly expectedOutput?: string | null;
135
+ readonly handoffSummary?: string | null;
136
+ readonly dependencyReason?: string | null;
137
+ readonly pullRequestNumber?: number | null;
138
+ readonly targetBranch: string;
139
+ }
72
140
  export interface DispatchStoryDevelopContext {
73
141
  readonly storyWorkItemId: string;
74
142
  readonly planId: string;
@@ -125,6 +193,55 @@ export interface DispatchStoryQaDependencyContext {
125
193
  readonly fromWorkItemId: string;
126
194
  readonly toWorkItemId: string;
127
195
  }
196
+ export interface DispatchPriorPhaseArtifact {
197
+ readonly workItemId: string;
198
+ readonly artifactId: string;
199
+ readonly cycleRunId: string;
200
+ readonly phase: "implementation" | "self_review" | "validation";
201
+ readonly status: string;
202
+ readonly producedAt: string;
203
+ readonly sourcePath: string;
204
+ readonly verdictJson: string;
205
+ readonly report: string;
206
+ readonly verdictContentHash: string;
207
+ readonly reportContentHash: string;
208
+ }
209
+ export interface DispatchDesignerRefHandoffContext {
210
+ readonly schemaVersion: number;
211
+ readonly kind: string;
212
+ readonly ownerType: string;
213
+ readonly ownerId: string;
214
+ readonly projectId: string;
215
+ readonly repoId: string;
216
+ readonly title: string;
217
+ readonly refs: readonly DispatchDesignerRefAttachment[];
218
+ }
219
+ export interface DispatchDesignerRefAttachment {
220
+ readonly ref: DispatchDesignerRef;
221
+ readonly reason: string;
222
+ readonly required: boolean;
223
+ readonly source: string;
224
+ readonly lineageKind: string;
225
+ readonly inheritedFromItemType?: string | null;
226
+ readonly inheritedFromItemId?: string | null;
227
+ readonly inheritedFromDesignerRefId?: string | null;
228
+ readonly addedByLane: string;
229
+ readonly sortOrder: number;
230
+ readonly createdAt: string;
231
+ }
232
+ export interface DispatchDesignerRef {
233
+ readonly id: string;
234
+ readonly projectId: string;
235
+ readonly repoId: string;
236
+ readonly refKey: string;
237
+ readonly kind: string;
238
+ readonly path: string;
239
+ readonly anchor?: string | null;
240
+ readonly title: string;
241
+ readonly contentHash: string;
242
+ readonly commitSha: string;
243
+ readonly createdAt: string;
244
+ }
128
245
  export interface DispatchPullRequestContext {
129
246
  readonly number: number;
130
247
  readonly provider: string;
@@ -214,6 +331,11 @@ export interface ApiKeySecretGrant {
214
331
  readonly secret: string;
215
332
  readonly expiresAt: string;
216
333
  }
334
+ export interface ApiKeySecretGrantHandle {
335
+ readonly grantId: string;
336
+ readonly provider: string;
337
+ readonly expiresAt: string;
338
+ }
217
339
  export interface DispatchServerLocalConfig {
218
340
  readonly repoRoot: string;
219
341
  readonly cleanupWorktrees: boolean;