@loucompanion/forge-bridge 0.1.3-dev.f66395c3ac21 → 0.1.4-dev.04a1e5b9e484

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 (223) hide show
  1. package/README.md +165 -1
  2. package/dist/bridge/bridge-chat.types.d.ts +111 -0
  3. package/dist/bridge/bridge-chat.types.js +401 -0
  4. package/dist/bridge/bridge.base.d.ts +9 -1
  5. package/dist/bridge/bridge.service.d.ts +129 -3
  6. package/dist/bridge/bridge.service.js +2427 -70
  7. package/dist/bridge/bridge.types.d.ts +218 -2
  8. package/dist/bridge/dispatch-result-outbox.d.ts +25 -0
  9. package/dist/bridge/dispatch-result-outbox.js +62 -0
  10. package/dist/bridge/dispatch-result-worker.d.ts +2 -0
  11. package/dist/bridge/dispatch-result-worker.js +122 -0
  12. package/dist/bridge/internals.d.ts +2 -0
  13. package/dist/bridge/internals.js +1 -0
  14. package/dist/bridge/readiness-result-outbox.d.ts +21 -0
  15. package/dist/bridge/readiness-result-outbox.js +54 -0
  16. package/dist/bridge/readiness-result-worker.d.ts +2 -0
  17. package/dist/bridge/readiness-result-worker.js +121 -0
  18. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  19. package/dist/bridge/runner-host-capabilities.js +93 -0
  20. package/dist/cleanup/cleanup.service.d.ts +14 -2
  21. package/dist/cleanup/cleanup.service.js +496 -3
  22. package/dist/cleanup/cleanup.types.d.ts +10 -0
  23. package/dist/cleanup/index.d.ts +1 -1
  24. package/dist/cleanup/index.js +1 -1
  25. package/dist/cleanup/internals.d.ts +2 -2
  26. package/dist/cleanup/internals.js +1 -1
  27. package/dist/cli/cli.help.d.ts +2 -0
  28. package/dist/cli/cli.help.js +217 -0
  29. package/dist/cli/cli.service.d.ts +55 -7
  30. package/dist/cli/cli.service.js +429 -219
  31. package/dist/cli/local-config.d.ts +7 -0
  32. package/dist/cli/local-config.js +51 -3
  33. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  34. package/dist/runtime/aspnet-runtime-prebuild.js +341 -0
  35. package/dist/runtime/attempt-database-broker.d.ts +89 -0
  36. package/dist/runtime/attempt-database-broker.js +390 -0
  37. package/dist/runtime/attempt-node-runtime-launcher.d.ts +18 -0
  38. package/dist/runtime/attempt-node-runtime-launcher.js +140 -0
  39. package/dist/runtime/attempt-path-operation.d.ts +21 -0
  40. package/dist/runtime/attempt-path-operation.js +94 -0
  41. package/dist/runtime/attempt-private-directory.d.ts +6 -0
  42. package/dist/runtime/attempt-private-directory.js +54 -0
  43. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  44. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  45. package/dist/runtime/attempt-process-supervisor.d.ts +102 -0
  46. package/dist/runtime/attempt-process-supervisor.js +421 -0
  47. package/dist/runtime/canonical-source-file.d.ts +3 -0
  48. package/dist/runtime/canonical-source-file.js +54 -0
  49. package/dist/runtime/docker-engine-probe.d.ts +90 -0
  50. package/dist/runtime/docker-engine-probe.js +333 -0
  51. package/dist/runtime/generated-output-reset.d.ts +20 -0
  52. package/dist/runtime/generated-output-reset.js +157 -0
  53. package/dist/runtime/host-database-capability-boundary.d.ts +16 -0
  54. package/dist/runtime/host-database-capability-boundary.js +62 -0
  55. package/dist/runtime/host-diagnostics.d.ts +11 -0
  56. package/dist/runtime/host-diagnostics.js +192 -0
  57. package/dist/runtime/host-execution-root.d.ts +4 -0
  58. package/dist/runtime/host-execution-root.js +39 -0
  59. package/dist/runtime/host-listener-inspector.d.ts +51 -0
  60. package/dist/runtime/host-listener-inspector.js +385 -0
  61. package/dist/runtime/host-port-broker.d.ts +76 -0
  62. package/dist/runtime/host-port-broker.js +333 -0
  63. package/dist/runtime/host-runtime-contract.d.ts +58 -0
  64. package/dist/runtime/host-runtime-contract.js +813 -0
  65. package/dist/runtime/host-runtime-lease.d.ts +217 -0
  66. package/dist/runtime/host-runtime-lease.js +2487 -0
  67. package/dist/runtime/host-runtime-promotion.d.ts +32 -0
  68. package/dist/runtime/host-runtime-promotion.js +206 -0
  69. package/dist/runtime/memory-capacity.d.ts +70 -0
  70. package/dist/runtime/memory-capacity.js +123 -0
  71. package/dist/runtime/native-test-postgres-fallback.d.ts +25 -0
  72. package/dist/runtime/native-test-postgres-fallback.js +91 -0
  73. package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
  74. package/dist/runtime/node-host-runtime-adapter.js +286 -0
  75. package/dist/runtime/pinned-directory-process.d.ts +9 -0
  76. package/dist/runtime/pinned-directory-process.js +61 -0
  77. package/dist/runtime/pinned-source-directory.d.ts +17 -0
  78. package/dist/runtime/pinned-source-directory.js +94 -0
  79. package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
  80. package/dist/runtime/playwright-host-runtime-adapter.js +268 -0
  81. package/dist/runtime/prepared-runtime-identity.d.ts +6 -0
  82. package/dist/runtime/prepared-runtime-identity.js +218 -0
  83. package/dist/runtime/prepared-runtime-readiness.d.ts +30 -0
  84. package/dist/runtime/prepared-runtime-readiness.js +285 -0
  85. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  86. package/dist/runtime/runtime-isolation-lease.js +161 -0
  87. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  88. package/dist/runtime/workspace-resource-estimator.js +174 -0
  89. package/dist/sandbox/command-runner.d.ts +4 -0
  90. package/dist/sandbox/command-runner.js +47 -0
  91. package/dist/sandbox/contract.d.ts +5 -0
  92. package/dist/sandbox/contract.js +276 -0
  93. package/dist/sandbox/event-budget.d.ts +11 -0
  94. package/dist/sandbox/event-budget.js +27 -0
  95. package/dist/sandbox/image-policy.d.ts +35 -0
  96. package/dist/sandbox/image-policy.js +247 -0
  97. package/dist/sandbox/index.d.ts +5 -0
  98. package/dist/sandbox/index.js +5 -0
  99. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  100. package/dist/sandbox/lease-watchdog.js +67 -0
  101. package/dist/sandbox/preflight.d.ts +84 -0
  102. package/dist/sandbox/preflight.js +521 -0
  103. package/dist/sandbox/process.d.ts +20 -0
  104. package/dist/sandbox/process.js +209 -0
  105. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  106. package/dist/sandbox/provider-auth/codex.js +212 -0
  107. package/dist/sandbox/repository-lock.d.ts +6 -0
  108. package/dist/sandbox/repository-lock.js +129 -0
  109. package/dist/sandbox/runtime.d.ts +302 -0
  110. package/dist/sandbox/runtime.js +2368 -0
  111. package/dist/sandbox/sandbox.types.d.ts +93 -0
  112. package/dist/sandbox/sandbox.types.js +1 -0
  113. package/dist/sandbox/smoke.d.ts +76 -0
  114. package/dist/sandbox/smoke.js +380 -0
  115. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  116. package/dist/sandbox/worker-supervisor.js +36 -0
  117. package/dist/sandbox/worker.d.ts +1 -0
  118. package/dist/sandbox/worker.js +225 -0
  119. package/dist/sandbox/workspace.service.d.ts +13 -0
  120. package/dist/sandbox/workspace.service.js +74 -0
  121. package/dist/session/managed-attempt-tools.d.ts +28 -0
  122. package/dist/session/managed-attempt-tools.js +1016 -0
  123. package/dist/session/managed-build-coordinator.d.ts +5 -0
  124. package/dist/session/managed-build-coordinator.js +106 -0
  125. package/dist/session/managed-build-receipt.d.ts +21 -0
  126. package/dist/session/managed-build-receipt.js +337 -0
  127. package/dist/session/managed-verification-timeouts.d.ts +8 -0
  128. package/dist/session/managed-verification-timeouts.js +17 -0
  129. package/dist/session/phase-completion.artifacts.d.ts +27 -0
  130. package/dist/session/phase-completion.artifacts.js +829 -0
  131. package/dist/session/phase-completion.d.ts +53 -42
  132. package/dist/session/phase-completion.helpers.d.ts +117 -0
  133. package/dist/session/phase-completion.helpers.js +707 -0
  134. package/dist/session/phase-completion.js +125 -3007
  135. package/dist/session/phase-completion.prompts.d.ts +14 -0
  136. package/dist/session/phase-completion.prompts.js +233 -0
  137. package/dist/session/phase-completion.runtime.d.ts +49 -0
  138. package/dist/session/phase-completion.runtime.js +840 -0
  139. package/dist/session/phase-completion.schemas.d.ts +3 -0
  140. package/dist/session/phase-completion.schemas.js +859 -0
  141. package/dist/session/phase-completion.validation.d.ts +38 -0
  142. package/dist/session/phase-completion.validation.js +918 -0
  143. package/dist/session/pre-publication-validation.d.ts +75 -0
  144. package/dist/session/pre-publication-validation.js +661 -0
  145. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.d.ts +72 -0
  146. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.js +462 -0
  147. package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
  148. package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
  149. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +95 -0
  150. package/dist/session/provider-cli/provider-cli.artifacts.js +812 -0
  151. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  152. package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
  153. package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
  154. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +14 -0
  155. package/dist/session/provider-cli/provider-cli.deterministic.js +407 -0
  156. package/dist/session/provider-cli/provider-cli.process.d.ts +99 -0
  157. package/dist/session/provider-cli/provider-cli.process.js +874 -0
  158. package/dist/session/provider-cli/provider-cli.prompts.d.ts +52 -0
  159. package/dist/session/provider-cli/provider-cli.prompts.js +715 -0
  160. package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
  161. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +66 -0
  162. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  163. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  164. package/dist/session/provider-cli/provider-cli.runtime.d.ts +22 -0
  165. package/dist/session/provider-cli/provider-cli.runtime.js +92 -0
  166. package/dist/session/provider-cli/provider-cli.service.d.ts +47 -53
  167. package/dist/session/provider-cli/provider-cli.service.js +1258 -2497
  168. package/dist/session/provider-cli/provider-cli.types.d.ts +8 -0
  169. package/dist/session/provider-cli/provider-output-state.d.ts +32 -0
  170. package/dist/session/provider-cli/provider-output-state.js +817 -0
  171. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  172. package/dist/session/provider-cli/provider-usage.js +28 -5
  173. package/dist/session/session-redaction.d.ts +3 -0
  174. package/dist/session/session-redaction.js +448 -0
  175. package/dist/session/session.base.d.ts +6 -1
  176. package/dist/session/session.service.d.ts +8 -2
  177. package/dist/session/session.service.js +586 -59
  178. package/dist/session/session.types.d.ts +324 -3
  179. package/dist/session/session.types.js +939 -10
  180. package/dist/session/unreferenced-exports.d.ts +12 -0
  181. package/dist/session/unreferenced-exports.js +59 -0
  182. package/dist/shared/format/format-bytes.d.ts +1 -0
  183. package/dist/shared/format/format-bytes.js +9 -0
  184. package/dist/shared/git/git.js +2 -1
  185. package/dist/shared/path/path.d.ts +1 -0
  186. package/dist/shared/path/path.js +21 -1
  187. package/dist/shared/process/dotnet-toolchain.d.ts +17 -0
  188. package/dist/shared/process/dotnet-toolchain.js +307 -0
  189. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  190. package/dist/shared/process/host-process-recovery.js +79 -0
  191. package/dist/shared/process/host-process-supervisor.d.ts +59 -0
  192. package/dist/shared/process/host-process-supervisor.js +331 -0
  193. package/dist/shared/process/process.d.ts +3 -0
  194. package/dist/shared/process/process.js +13 -7
  195. package/dist/shared/process/windows-launch.d.ts +8 -0
  196. package/dist/shared/process/windows-launch.js +41 -0
  197. package/dist/shared/provider-capabilities.d.ts +4 -0
  198. package/dist/shared/provider-capabilities.js +88 -0
  199. package/dist/shared/redaction/redaction.d.ts +1 -0
  200. package/dist/shared/redaction/redaction.js +47 -4
  201. package/dist/shared/time/monotonic.d.ts +1 -0
  202. package/dist/shared/time/monotonic.js +8 -0
  203. package/dist/version.d.ts +1 -1
  204. package/dist/version.js +1 -1
  205. package/dist/worktree/artifact-injector.d.ts +3 -2
  206. package/dist/worktree/artifact-injector.js +82 -10
  207. package/dist/worktree/dependency-warmer.d.ts +53 -1
  208. package/dist/worktree/dependency-warmer.js +619 -11
  209. package/dist/worktree/index.d.ts +2 -1
  210. package/dist/worktree/index.js +1 -0
  211. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  212. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  213. package/dist/worktree/story-qa-reference-hydrator.d.ts +4 -0
  214. package/dist/worktree/story-qa-reference-hydrator.js +253 -0
  215. package/dist/worktree/worktree-paths.d.ts +4 -0
  216. package/dist/worktree/worktree-paths.js +30 -0
  217. package/dist/worktree/worktree.base.d.ts +3 -0
  218. package/dist/worktree/worktree.service.d.ts +16 -3
  219. package/dist/worktree/worktree.service.js +334 -54
  220. package/dist/worktree/worktree.types.d.ts +9 -1
  221. package/package.json +12 -7
  222. package/runtime/windows-job-supervisor.ps1 +722 -0
  223. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -1,7 +1,41 @@
1
- import type { RunOnceResultPacket, RunnerDispatchEvent } from "../session/session.types.js";
1
+ import type { ApiKeySecretGrant, DispatchSandboxResourceRequest, RunOnceResultPacket, RunnerDispatchEvent } from "../session/session.types.js";
2
+ import type { SandboxFeatureMatrix } from "../sandbox/sandbox.types.js";
3
+ import type { HostRuntimeCapacitySnapshot } from "../runtime/host-runtime-lease.js";
4
+ import type { MemoryAdmissionDiagnostics } from "../runtime/memory-capacity.js";
5
+ import type { DockerEngineCapabilitySnapshot } from "../runtime/docker-engine-probe.js";
6
+ import type { BridgeChatCapability, ChatTurnCancelPacket } from "./bridge-chat.types.js";
2
7
  export interface BridgeServiceConfig {
3
8
  readonly serverUrl: string;
4
9
  readonly runnerId: string;
10
+ readonly runnerInstanceId: string;
11
+ readonly trustDomainId: string;
12
+ readonly maxConcurrentSandboxes: number;
13
+ readonly maxConcurrentSandboxBuilds: number;
14
+ readonly sandboxPidCapacity: number;
15
+ readonly dockerEnabled: boolean;
16
+ readonly allowOpenPublicSandbox: boolean;
17
+ readonly sandboxPublicationMode?: "bridge_push" | "shared_filesystem";
18
+ readonly dockerExecutable: string;
19
+ readonly dockerContext: string;
20
+ readonly dockerDesktopRecoveryEnabled: boolean;
21
+ readonly dockerRecoveryFailureThreshold: number;
22
+ readonly dockerRecoveryCooldownMs: number;
23
+ readonly dockerRecoveryStartupTimeoutMs: number;
24
+ readonly sandboxStateRoot: string;
25
+ readonly sandboxExecutionRoot: string;
26
+ readonly requireRootlessDocker: boolean;
27
+ readonly diskHighWatermarkPercent: number;
28
+ readonly diskLowWatermarkPercent: number;
29
+ readonly minimumWorkspaceBytes: number;
30
+ readonly sandboxHostMemoryReserveBytes?: number;
31
+ readonly defaultSandboxWorkerMemoryLimitBytes: number;
32
+ readonly defaultSandboxServiceMemoryLimitBytes: number;
33
+ readonly sandboxRuntimeOverheadBytes: number;
34
+ readonly sandboxMemorySampleIntervalMs: number;
35
+ readonly imageRetentionCount: number;
36
+ readonly imageRetentionTtlMs: number;
37
+ readonly auditRetentionCount: number;
38
+ readonly auditRetentionTtlMs: number;
5
39
  readonly deviceToken: string;
6
40
  readonly deviceName: string;
7
41
  readonly defaultRepoRoot: string;
@@ -17,10 +51,15 @@ export interface BridgeServiceConfig {
17
51
  readonly supportsManagedForgeArtifacts: boolean;
18
52
  readonly supportsRunnerAuthoredPhaseArtifacts: boolean;
19
53
  readonly supportsDraftRunOnce: boolean;
54
+ readonly supportsDraftAttachments: boolean;
55
+ readonly supportsDraftImageAttachments: boolean;
56
+ readonly bridgeChat: BridgeChatCapability;
20
57
  readonly version: string;
21
58
  readonly protocolVersion: number;
22
59
  readonly minServerVersion?: string | null;
23
60
  readonly heartbeatIntervalMs: number;
61
+ readonly callbackAckTimeoutMs?: number;
62
+ readonly heartbeatDockerProbeWaitMs?: number;
24
63
  readonly reconnectBackoffMs: readonly number[];
25
64
  }
26
65
  export interface BridgeRepoMapping {
@@ -35,6 +74,7 @@ export interface QaAppLaunchTarget {
35
74
  readonly command?: string | null;
36
75
  readonly working_directory?: string | null;
37
76
  readonly ready_url?: string | null;
77
+ readonly managed_runtime_port?: string | null;
38
78
  readonly routes?: readonly string[] | null;
39
79
  readonly notes?: string | null;
40
80
  }
@@ -43,6 +83,20 @@ export interface BridgeProviderStatus {
43
83
  readonly version?: string | null;
44
84
  readonly models?: readonly string[] | null;
45
85
  }
86
+ export interface BridgeToolCapability {
87
+ readonly available: boolean;
88
+ readonly executable: string;
89
+ readonly version?: string | null;
90
+ }
91
+ export interface BridgeToolchainCapabilities {
92
+ readonly node: BridgeToolCapability;
93
+ readonly pnpm: BridgeToolCapability;
94
+ readonly dotnet: BridgeToolCapability;
95
+ readonly git: BridgeToolCapability;
96
+ readonly bash: BridgeToolCapability;
97
+ readonly powershell: BridgeToolCapability;
98
+ readonly docker?: BridgeToolCapability | null;
99
+ }
46
100
  export interface BridgeCapabilities {
47
101
  readonly schemaVersion?: number | null;
48
102
  readonly activeSessionCount?: number | null;
@@ -52,17 +106,74 @@ export interface BridgeCapabilities {
52
106
  readonly repoMappings?: readonly BridgeRepoMapping[] | null;
53
107
  readonly os?: string | null;
54
108
  readonly sandboxProfiles?: readonly string[] | null;
109
+ readonly runnerInstanceId?: string | null;
110
+ readonly trustDomainId?: string | null;
111
+ readonly executionFacilities?: readonly string[] | null;
112
+ readonly dockerEngine?: DockerEngineCapabilitySnapshot | null;
113
+ readonly sandboxRuntimes?: readonly string[] | null;
114
+ readonly sandboxRuntimeVersion?: string | null;
115
+ readonly sandboxFeatures?: SandboxFeatureMatrix | null;
116
+ readonly allowsOpenPublicSandbox?: boolean | null;
117
+ readonly sandboxPublicationModes?: readonly string[] | null;
118
+ readonly sandboxAuthModes?: Readonly<Record<string, readonly string[]>> | null;
119
+ readonly maxConcurrentSandboxes?: number | null;
120
+ readonly reservedSandboxCount?: number | null;
121
+ readonly activeSandboxCount?: number | null;
122
+ readonly startingSandboxCount?: number | null;
123
+ readonly degradedReservationCount?: number | null;
124
+ readonly availableMemoryBytes?: number | null;
125
+ readonly availableCpuMillis?: number | null;
126
+ readonly availableWorkspaceBytes?: number | null;
127
+ readonly diskLow?: boolean | null;
128
+ readonly preflightGeneration?: number | null;
129
+ readonly sandboxUnavailableCode?: string | null;
130
+ readonly sandboxUnavailableReason?: string | null;
131
+ readonly sandboxMemoryCapacity?: SandboxMemoryCapacityDiagnostics | null;
132
+ readonly hostRuntime?: HostRuntimeCapacitySnapshot | null;
55
133
  readonly supportsApiSecretGrant?: boolean | null;
56
134
  readonly supportsManagedWorktrees?: boolean | null;
57
135
  readonly supportsManagedForgeArtifacts?: boolean | null;
58
136
  readonly supportsRunnerAuthoredPhaseArtifacts?: boolean | null;
59
137
  readonly supportsDraftRunOnce?: boolean | null;
138
+ readonly supportsDraftAttachments?: boolean | null;
139
+ readonly supportsDraftImageAttachments?: boolean | null;
140
+ readonly bridgeChat?: BridgeChatCapability | null;
141
+ readonly toolchains?: BridgeToolchainCapabilities | null;
142
+ readonly playwrightProjects?: readonly string[] | null;
143
+ readonly browserLaunch?: boolean | null;
144
+ readonly providerConnectivity?: Readonly<Record<string, boolean>> | null;
145
+ }
146
+ export interface SandboxMemoryCapacityDiagnostics {
147
+ readonly defaultWorkerMemoryLimitBytes: number;
148
+ readonly defaultServiceMemoryLimitBytes: number;
149
+ readonly runtimeOverheadBytes: number;
150
+ readonly hostReserveBytes: number;
151
+ readonly activeReservationBytes: number;
152
+ readonly daemonTotalBytes: number;
153
+ readonly daemonAvailableBytes: number;
154
+ readonly hostAvailableBytes: number;
155
+ readonly conservativeAvailableBytes: number;
156
+ readonly availableForAdmissionBytes: number;
157
+ readonly memoryAdmission: MemoryAdmissionDiagnostics;
60
158
  }
61
159
  export interface BridgeHelloRequest {
62
160
  readonly version: string;
63
161
  readonly protocolVersion: number;
64
162
  readonly capabilities: BridgeCapabilities;
65
163
  readonly minServerVersion?: string | null;
164
+ readonly runnerInstanceId: string;
165
+ }
166
+ export interface BridgePublishCredential {
167
+ readonly username: string;
168
+ readonly secret: string;
169
+ readonly remoteUrl: string;
170
+ readonly expiresAt: string;
171
+ }
172
+ export interface BridgePublishGrantRedemptionResponse {
173
+ readonly accepted: boolean;
174
+ readonly code?: string | null;
175
+ readonly message?: string | null;
176
+ readonly credential?: BridgePublishCredential | null;
66
177
  }
67
178
  export interface BridgeHeartbeatRequest {
68
179
  readonly status?: string | null;
@@ -78,17 +189,122 @@ export interface BridgeCallbackAck {
78
189
  export interface BridgeDispatchEventCallback {
79
190
  readonly leaseId: string;
80
191
  readonly fencingToken: string;
192
+ readonly dispatchAttemptId?: string | null;
193
+ readonly runnerInstanceId?: string | null;
81
194
  readonly event: RunnerDispatchEvent;
82
195
  }
83
196
  export interface BridgeDispatchResultCallback {
84
197
  readonly leaseId: string;
85
198
  readonly fencingToken: string;
199
+ readonly dispatchAttemptId?: string | null;
200
+ readonly runnerInstanceId?: string | null;
86
201
  readonly result: RunOnceResultPacket;
87
202
  }
88
- export interface BridgeCancelRequest {
203
+ export interface BridgeOfferDecisionCallback {
204
+ readonly dispatchAttemptId: string;
205
+ readonly leaseId: string;
206
+ readonly fencingToken: string;
207
+ readonly runnerInstanceId: string;
208
+ readonly accepted: boolean;
209
+ readonly code?: string | null;
210
+ readonly message?: string | null;
211
+ readonly reservedUntil?: string | null;
212
+ readonly reservedResources?: DispatchSandboxResourceRequest | null;
213
+ readonly sandboxConfigCommitSha?: string | null;
214
+ }
215
+ export interface BridgeDispatchStartedCallback {
216
+ readonly dispatchAttemptId: string;
217
+ readonly leaseId: string;
218
+ readonly fencingToken: string;
219
+ readonly runnerInstanceId: string;
220
+ }
221
+ export interface BridgeSecretGrantRedemptionRequest {
222
+ readonly dispatchAttemptId: string;
223
+ readonly leaseId: string;
224
+ readonly fencingToken: string;
225
+ readonly runnerInstanceId: string;
226
+ readonly grantId: string;
227
+ }
228
+ export interface BridgeSecretGrantRedemptionResponse extends BridgeCallbackAck {
229
+ readonly grant?: ApiKeySecretGrant | null;
230
+ }
231
+ export interface BridgeLeaseUpdateRequest {
232
+ readonly dispatchAttemptId: string;
233
+ readonly leaseId: string;
234
+ readonly fencingToken: string;
235
+ readonly runnerInstanceId: string;
236
+ readonly expiresAt: string;
237
+ }
238
+ export interface BridgeCancelRequest extends ChatTurnCancelPacket {
239
+ /** When present, cancels only this pre-start reservation, never an active session. */
240
+ readonly dispatchAttemptId?: string | null;
89
241
  readonly leaseId: string;
90
242
  readonly fencingToken: string;
91
243
  readonly reason?: string | null;
92
244
  }
245
+ export interface BridgeReapRequest {
246
+ readonly runnerId: string;
247
+ readonly runnerInstanceId: string;
248
+ readonly requestedAt: string;
249
+ readonly reason?: string | null;
250
+ }
251
+ export interface BridgeAttemptSnapshotItem {
252
+ readonly dispatchAttemptId: string;
253
+ readonly fencingToken: string;
254
+ readonly runnerInstanceId: string;
255
+ readonly status: string;
256
+ readonly expiresAt: string;
257
+ }
258
+ export interface BridgeAttemptSnapshotResponse extends BridgeCallbackAck {
259
+ readonly attempts?: readonly BridgeAttemptSnapshotItem[] | null;
260
+ readonly generatedAt?: string | null;
261
+ }
93
262
  export type BridgeDispatchHandler = (dispatch: unknown) => Promise<void>;
263
+ export type BridgeChatTurnHandler = (dispatch: unknown) => Promise<void>;
264
+ export type BridgeOfferHandler = (dispatch: unknown) => Promise<void>;
265
+ export type BridgeStartHandler = (dispatch: unknown) => Promise<void>;
266
+ export type BridgeLeaseUpdateHandler = (request: BridgeLeaseUpdateRequest) => Promise<void>;
94
267
  export type BridgeCancelHandler = (request: BridgeCancelRequest) => Promise<void>;
268
+ export type BridgeReapHandler = (request: BridgeReapRequest) => Promise<void>;
269
+ export type BridgeReadinessProbeHandler = (request: unknown) => Promise<void>;
270
+ export interface BridgeRuntimeReadinessRequest {
271
+ readonly schemaVersion: 1;
272
+ readonly requestId: string;
273
+ readonly fencingToken: string;
274
+ readonly userId: string;
275
+ readonly projectId: string;
276
+ readonly repoId: string;
277
+ readonly runnerInstanceId: string;
278
+ readonly attempt: number;
279
+ readonly repoName: string;
280
+ readonly remoteUrl: string;
281
+ readonly defaultBranch: string;
282
+ readonly cloneOnDemand: boolean;
283
+ readonly runnerRepoLocalPath: string;
284
+ readonly revision: string;
285
+ readonly expiresAt: string;
286
+ }
287
+ export interface BridgeRuntimeReadinessResult {
288
+ readonly schemaVersion: 1;
289
+ readonly requestId: string;
290
+ readonly fencingToken: string;
291
+ readonly runnerInstanceId: string;
292
+ readonly status: "parallel_ready" | "serialized" | "failed";
293
+ readonly runtimeContractFingerprint?: string | null;
294
+ readonly runtimeContractSource?: string | null;
295
+ readonly reasonCode?: string | null;
296
+ readonly reasonMessage?: string | null;
297
+ readonly qaAppLaunchTarget?: BridgeRuntimeReadinessQaLaunchTarget | null;
298
+ readonly checkedAt: string;
299
+ }
300
+ export interface BridgeRuntimeReadinessAccepted {
301
+ readonly schemaVersion: 1;
302
+ readonly requestId: string;
303
+ readonly fencingToken: string;
304
+ readonly runnerInstanceId: string;
305
+ readonly acceptedAt: string;
306
+ }
307
+ export interface BridgeRuntimeReadinessQaLaunchTarget {
308
+ readonly managedRuntimePort: string;
309
+ readonly routes: readonly string[];
310
+ }
@@ -0,0 +1,25 @@
1
+ import type { BridgeDispatchResultCallback } from "./bridge.types.js";
2
+ export interface DurableDispatchResult {
3
+ readonly schemaVersion: 1;
4
+ readonly persistedAt: string;
5
+ readonly payload: BridgeDispatchResultCallback;
6
+ }
7
+ export interface DispatchResultOutbox {
8
+ persist(payload: BridgeDispatchResultCallback): Promise<void>;
9
+ pending(): Promise<readonly DurableDispatchResult[]>;
10
+ acknowledge(leaseId: string): Promise<void>;
11
+ }
12
+ /**
13
+ * Persists the already-redacted terminal result before the Bridge attempts its
14
+ * SignalR callback. A transport or API restart can therefore delay delivery,
15
+ * but cannot turn successful provider work into a fabricated failed result.
16
+ */
17
+ export declare class FileDispatchResultOutbox implements DispatchResultOutbox {
18
+ private readonly stateRoot;
19
+ private readonly writes;
20
+ constructor(stateRoot: string);
21
+ persist(payload: BridgeDispatchResultCallback): Promise<void>;
22
+ pending(): Promise<readonly DurableDispatchResult[]>;
23
+ acknowledge(leaseId: string): Promise<void>;
24
+ private operate;
25
+ }
@@ -0,0 +1,62 @@
1
+ import path from "node:path";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { assertCanonicalDirectory, createCanonicalDirectory } from "../runtime/attempt-private-directory.js";
4
+ import { runPinnedDirectoryProcess } from "../runtime/pinned-directory-process.js";
5
+ import { dispatchResultReceiptWorker } from "./dispatch-result-worker.js";
6
+ /**
7
+ * Persists the already-redacted terminal result before the Bridge attempts its
8
+ * SignalR callback. A transport or API restart can therefore delay delivery,
9
+ * but cannot turn successful provider work into a fabricated failed result.
10
+ */
11
+ export class FileDispatchResultOutbox {
12
+ stateRoot;
13
+ writes = new Map();
14
+ constructor(stateRoot) {
15
+ this.stateRoot = stateRoot;
16
+ }
17
+ async persist(payload) {
18
+ const name = recordName(payload.leaseId);
19
+ const value = JSON.parse(JSON.stringify({ payload }));
20
+ const pending = this.writes.get(name);
21
+ if (pending) {
22
+ if (!isDeepStrictEqual(pending.value, value)) {
23
+ throw new Error("dispatch_result_conflict: lease identity reused with a different result.");
24
+ }
25
+ return await pending.write;
26
+ }
27
+ const write = this.operate({
28
+ kind: "persist",
29
+ name,
30
+ record: { schemaVersion: 1, persistedAt: new Date().toISOString(), ...value },
31
+ }).then(() => undefined);
32
+ this.writes.set(name, { value, write });
33
+ try {
34
+ await write;
35
+ }
36
+ finally {
37
+ if (this.writes.get(name)?.write === write)
38
+ this.writes.delete(name);
39
+ }
40
+ }
41
+ async pending() {
42
+ return JSON.parse(await this.operate({ kind: "pending" }));
43
+ }
44
+ async acknowledge(leaseId) {
45
+ await this.operate({ kind: "acknowledge", name: recordName(leaseId) });
46
+ }
47
+ async operate(operation) {
48
+ const root = path.resolve(this.stateRoot, "dispatch-results");
49
+ await createCanonicalDirectory(root);
50
+ const identity = await assertCanonicalDirectory(root);
51
+ if (!identity)
52
+ throw new Error("dispatch_result_invalid: outbox directory is missing.");
53
+ return await runPinnedDirectoryProcess(root, identity, dispatchResultReceiptWorker, operation, undefined, 256 * 1024 * 1024);
54
+ }
55
+ }
56
+ function recordName(leaseId) {
57
+ const value = leaseId.replaceAll("-", "").toLowerCase();
58
+ if (!/^[0-9a-f]{32}$/u.test(value)) {
59
+ throw new Error("dispatch_result_invalid: lease id must be a UUID.");
60
+ }
61
+ return `${value}.json`;
62
+ }
@@ -0,0 +1,2 @@
1
+ /** Runs in a bounded async child. Receipt IO stays relative to verified, pinned cwd. */
2
+ export declare const dispatchResultReceiptWorker: string;
@@ -0,0 +1,122 @@
1
+ /** Runs in a bounded async child. Receipt IO stays relative to verified, pinned cwd. */
2
+ export const dispatchResultReceiptWorker = String.raw `
3
+ const fs = require('node:fs');
4
+ const { randomUUID } = require('node:crypto');
5
+ const { isDeepStrictEqual } = require('node:util');
6
+ const job = JSON.parse(fs.readFileSync(0, 'utf8'));
7
+ const maximumBytes = 64 * 1024 * 1024;
8
+ const same = (a, b) => a.dev === b.dev && a.ino === b.ino;
9
+ const sameReadMetadata = (a, b) => a.size === b.size && a.mtimeNs === b.mtimeNs
10
+ && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.birthtimeNs === b.birthtimeNs;
11
+ const samePublicationMetadata = (a, b) => a.ctimeNs === b.ctimeNs && a.nlink === b.nlink;
12
+ const validDate = value => typeof value === 'string' && Number.isFinite(Date.parse(value));
13
+ const uuid = value => typeof value === 'string' && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value);
14
+ const validName = name => typeof name === 'string' && /^[0-9a-f]{32}\.json$/.test(name);
15
+ const validRecord = (record, name) => record && record.schemaVersion === 1
16
+ && validDate(record.persistedAt) && record.payload
17
+ && uuid(record.payload.leaseId) && uuid(record.payload.fencingToken)
18
+ && (record.payload.dispatchAttemptId == null || uuid(record.payload.dispatchAttemptId))
19
+ && (record.payload.runnerInstanceId == null || uuid(record.payload.runnerInstanceId))
20
+ && record.payload.result && Number.isInteger(record.payload.result.exitCode)
21
+ && typeof record.payload.result.summary === 'string'
22
+ && Array.isArray(record.payload.result.events)
23
+ && record.payload.leaseId.replaceAll('-', '').toLowerCase() + '.json' === name;
24
+ const read = name => {
25
+ if (!validName(name)) throw new Error('dispatch_result_invalid: invalid name');
26
+ let before;
27
+ try { before = fs.lstatSync(name, { bigint: true }); }
28
+ catch (error) { if (error.code === 'ENOENT') return null; throw error; }
29
+ if (!before.isFile() || before.isSymbolicLink() || before.size > BigInt(maximumBytes))
30
+ throw new Error('dispatch_result_invalid: not a bounded regular file');
31
+ const fd = fs.openSync(name, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
32
+ try {
33
+ const opened = fs.fstatSync(fd, { bigint: true });
34
+ if (!same(before, opened) || !opened.isFile() || opened.size !== before.size)
35
+ throw new Error('dispatch_result_invalid: receipt identity changed');
36
+ let baseline = opened;
37
+ let firstRead;
38
+ for (let attempt = 0; attempt < 2; attempt++) {
39
+ const bytes = Buffer.alloc(Number(opened.size));
40
+ let offset = 0;
41
+ while (offset < bytes.length) {
42
+ const count = fs.readSync(fd, bytes, offset, bytes.length - offset, offset);
43
+ if (!count) throw new Error('dispatch_result_invalid: incomplete receipt');
44
+ offset += count;
45
+ }
46
+ const after = fs.fstatSync(fd, { bigint: true });
47
+ const current = fs.lstatSync(name, { bigint: true });
48
+ if (!same(opened, after) || !same(after, current) || !current.isFile() || current.isSymbolicLink()
49
+ || !sameReadMetadata(baseline, after) || !sameReadMetadata(after, current))
50
+ throw new Error('dispatch_result_invalid: receipt changed during read');
51
+ if (!samePublicationMetadata(baseline, after) || !samePublicationMetadata(after, current)) {
52
+ // A publisher may unlink its temporary hard link between either stat.
53
+ // This is only a retry fence, never permission to trust unstable bytes.
54
+ if (attempt !== 0 || job.kind !== 'persist' || baseline.nlink !== 2n || current.nlink !== 1n
55
+ || baseline.ctimeNs === current.ctimeNs
56
+ || (!samePublicationMetadata(baseline, after) && !samePublicationMetadata(after, current)))
57
+ throw new Error('dispatch_result_invalid: receipt changed during read');
58
+ firstRead = bytes;
59
+ baseline = current;
60
+ continue;
61
+ }
62
+ if (firstRead && !bytes.equals(firstRead))
63
+ throw new Error('dispatch_result_invalid: receipt changed during read');
64
+ const record = JSON.parse(bytes.toString('utf8'));
65
+ if (!validRecord(record, name)) throw new Error('dispatch_result_invalid: malformed or wrong identity');
66
+ return record;
67
+ }
68
+ throw new Error('dispatch_result_invalid: receipt changed during read');
69
+ } finally { fs.closeSync(fd); }
70
+ };
71
+ const identical = (existing, candidate) => {
72
+ if (!isDeepStrictEqual(existing.payload, candidate.payload))
73
+ throw new Error('dispatch_result_conflict: lease identity reused with a different result');
74
+ };
75
+ try {
76
+ const cwd = fs.statSync('.', { bigint: true });
77
+ const real = fs.realpathSync('.');
78
+ if (!same(cwd, { dev: BigInt(job.dev), ino: BigInt(job.ino) })
79
+ || (process.platform === 'win32' ? real.toLowerCase() !== job.parent.toLowerCase() : real !== job.parent))
80
+ throw new Error('dispatch_result_invalid: parent identity changed');
81
+ if (job.kind === 'persist') {
82
+ if (!validName(job.name) || !validRecord(job.record, job.name)) throw new Error('dispatch_result_invalid: invalid candidate');
83
+ const content = JSON.stringify(job.record) + '\n';
84
+ if (Buffer.byteLength(content) > maximumBytes) throw new Error('dispatch_result_invalid: receipt too large');
85
+ const existing = read(job.name);
86
+ if (existing) identical(existing, job.record);
87
+ else {
88
+ const temporary = job.name + '.' + randomUUID() + '.tmp';
89
+ let owned = false;
90
+ try {
91
+ const fd = fs.openSync(temporary, 'wx', 0o600);
92
+ owned = true;
93
+ try { fs.writeFileSync(fd, content, 'utf8'); fs.fsyncSync(fd); }
94
+ finally { fs.closeSync(fd); }
95
+ try { fs.linkSync(temporary, job.name); }
96
+ catch (error) {
97
+ if (error.code !== 'EEXIST') throw error;
98
+ const winner = read(job.name);
99
+ if (!winner) throw error;
100
+ identical(winner, job.record);
101
+ }
102
+ } finally { if (owned) fs.unlinkSync(temporary); }
103
+ }
104
+ } else if (job.kind === 'pending') {
105
+ const records = [];
106
+ const directory = fs.opendirSync('.');
107
+ try {
108
+ let entry;
109
+ while (records.length < 3 && (entry = directory.readSync()) !== null) {
110
+ if (!entry.isFile() || !validName(entry.name)) continue;
111
+ try { const record = read(entry.name); if (record) records.push(record); }
112
+ catch { /* Retain malformed evidence; never send it as a trusted callback. */ }
113
+ }
114
+ } finally { directory.closeSync(); }
115
+ records.sort((a, b) => a.persistedAt.localeCompare(b.persistedAt));
116
+ process.stdout.write(JSON.stringify(records));
117
+ } else if (job.kind === 'acknowledge') {
118
+ if (!validName(job.name)) throw new Error('dispatch_result_invalid: invalid name');
119
+ try { fs.unlinkSync(job.name); } catch (error) { if (error.code !== 'ENOENT') throw error; }
120
+ } else throw new Error('dispatch_result_invalid: invalid operation');
121
+ } catch (error) { process.stderr.write(error.message); process.exitCode = 1; }
122
+ `;
@@ -1,3 +1,5 @@
1
1
  export { SignalRBridgeConnection, SignalRBridgeConnectionFactory, defaultBridgeServiceConfig } from "./bridge.service.js";
2
+ export { bridgeChatProtocolVersion, bridgeChatUpdateRequiredCode, normalizeBridgeChatCapability, parseChatTurnDispatchPacket, unavailableBridgeChatCapability } from "./bridge-chat.types.js";
2
3
  export type { BridgeConnection, BridgeConnectionFactory } from "./bridge.base.js";
4
+ export type { BridgeChatCapability, ChatTurnDispatchPacket } from "./bridge-chat.types.js";
3
5
  export type { BridgeCallbackAck, BridgeCancelHandler, BridgeCancelRequest, BridgeCapabilities, BridgeDispatchEventCallback, BridgeDispatchHandler, BridgeDispatchResultCallback, BridgeHeartbeatRequest, BridgeHelloRequest, BridgeRepoMapping, BridgeServiceConfig } from "./bridge.types.js";
@@ -1 +1,2 @@
1
1
  export { SignalRBridgeConnection, SignalRBridgeConnectionFactory, defaultBridgeServiceConfig } from "./bridge.service.js";
2
+ export { bridgeChatProtocolVersion, bridgeChatUpdateRequiredCode, normalizeBridgeChatCapability, parseChatTurnDispatchPacket, unavailableBridgeChatCapability } from "./bridge-chat.types.js";
@@ -0,0 +1,21 @@
1
+ import type { BridgeRuntimeReadinessResult } from "./bridge.types.js";
2
+ export interface DurableReadinessResult {
3
+ readonly schemaVersion: 1;
4
+ readonly expiresAt: string;
5
+ readonly persistedAt: string;
6
+ readonly result: BridgeRuntimeReadinessResult;
7
+ }
8
+ export interface ReadinessResultOutbox {
9
+ persist(result: BridgeRuntimeReadinessResult, expiresAt: string): Promise<void>;
10
+ pending(): Promise<readonly DurableReadinessResult[]>;
11
+ acknowledge(requestId: string): Promise<void>;
12
+ }
13
+ export declare class FileReadinessResultOutbox implements ReadinessResultOutbox {
14
+ private readonly stateRoot;
15
+ private readonly writes;
16
+ constructor(stateRoot: string);
17
+ persist(result: BridgeRuntimeReadinessResult, expiresAt: string): Promise<void>;
18
+ pending(): Promise<readonly DurableReadinessResult[]>;
19
+ acknowledge(requestId: string): Promise<void>;
20
+ private operate;
21
+ }
@@ -0,0 +1,54 @@
1
+ import path from "node:path";
2
+ import { isDeepStrictEqual } from "node:util";
3
+ import { assertCanonicalDirectory, createCanonicalDirectory } from "../runtime/attempt-private-directory.js";
4
+ import { runPinnedDirectoryProcess } from "../runtime/pinned-directory-process.js";
5
+ import { readinessReceiptWorker } from "./readiness-result-worker.js";
6
+ export class FileReadinessResultOutbox {
7
+ stateRoot;
8
+ writes = new Map();
9
+ constructor(stateRoot) {
10
+ this.stateRoot = stateRoot;
11
+ }
12
+ async persist(result, expiresAt) {
13
+ const name = recordName(result.requestId);
14
+ // Capture caller-owned data before yielding. Compare semantic JSON, not key order.
15
+ const value = JSON.parse(JSON.stringify({ result, expiresAt }));
16
+ const pending = this.writes.get(name);
17
+ if (pending) {
18
+ if (!isDeepStrictEqual(pending.value, value))
19
+ throw new Error("runtime_readiness_result_conflict: request identity reused.");
20
+ return await pending.write;
21
+ }
22
+ const write = this.operate({ kind: "persist", name, record: {
23
+ schemaVersion: 1, persistedAt: new Date().toISOString(), ...value,
24
+ } }).then(() => undefined);
25
+ this.writes.set(name, { value, write });
26
+ try {
27
+ await write;
28
+ }
29
+ finally {
30
+ if (this.writes.get(name)?.write === write)
31
+ this.writes.delete(name);
32
+ }
33
+ }
34
+ async pending() {
35
+ return JSON.parse(await this.operate({ kind: "pending" }));
36
+ }
37
+ async acknowledge(requestId) {
38
+ await this.operate({ kind: "acknowledge", name: recordName(requestId) });
39
+ }
40
+ async operate(operation) {
41
+ const root = path.resolve(this.stateRoot, "readiness-results");
42
+ await createCanonicalDirectory(root);
43
+ const identity = await assertCanonicalDirectory(root);
44
+ if (!identity)
45
+ throw new Error("runtime_readiness_result_invalid: outbox directory is missing.");
46
+ return await runPinnedDirectoryProcess(root, identity, readinessReceiptWorker, operation, undefined, 256 * 256 * 1024 + 1024);
47
+ }
48
+ }
49
+ function recordName(requestId) {
50
+ const value = requestId.replaceAll("-", "").toLowerCase();
51
+ if (!/^[0-9a-f]{32}$/u.test(value))
52
+ throw new Error("runtime_readiness_result_invalid: request id must be a UUID.");
53
+ return `${value}.json`;
54
+ }
@@ -0,0 +1,2 @@
1
+ /** Runs in a bounded async child. Receipt IO stays relative to verified, pinned cwd. */
2
+ export declare const readinessReceiptWorker: string;