@loucompanion/forge-bridge 0.1.3-dev.b3bce6ba65a6 → 0.1.3-dev.ba293ccf876b

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 (189) hide show
  1. package/README.md +127 -0
  2. package/dist/bridge/bridge.base.d.ts +8 -1
  3. package/dist/bridge/bridge.service.d.ts +98 -3
  4. package/dist/bridge/bridge.service.js +1425 -45
  5. package/dist/bridge/bridge.types.d.ts +205 -1
  6. package/dist/bridge/readiness-result-outbox.d.ts +21 -0
  7. package/dist/bridge/readiness-result-outbox.js +54 -0
  8. package/dist/bridge/readiness-result-worker.d.ts +2 -0
  9. package/dist/bridge/readiness-result-worker.js +100 -0
  10. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  11. package/dist/bridge/runner-host-capabilities.js +93 -0
  12. package/dist/cleanup/cleanup.service.d.ts +14 -2
  13. package/dist/cleanup/cleanup.service.js +496 -3
  14. package/dist/cleanup/cleanup.types.d.ts +10 -0
  15. package/dist/cleanup/index.d.ts +1 -1
  16. package/dist/cleanup/index.js +1 -1
  17. package/dist/cleanup/internals.d.ts +2 -2
  18. package/dist/cleanup/internals.js +1 -1
  19. package/dist/cli/cli.help.js +47 -1
  20. package/dist/cli/cli.service.d.ts +45 -2
  21. package/dist/cli/cli.service.js +377 -27
  22. package/dist/cli/local-config.d.ts +5 -0
  23. package/dist/cli/local-config.js +45 -1
  24. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  25. package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
  26. package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
  27. package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
  28. package/dist/runtime/attempt-path-operation.d.ts +21 -0
  29. package/dist/runtime/attempt-path-operation.js +94 -0
  30. package/dist/runtime/attempt-private-directory.d.ts +6 -0
  31. package/dist/runtime/attempt-private-directory.js +54 -0
  32. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  33. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  34. package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
  35. package/dist/runtime/attempt-process-supervisor.js +386 -0
  36. package/dist/runtime/canonical-source-file.d.ts +3 -0
  37. package/dist/runtime/canonical-source-file.js +54 -0
  38. package/dist/runtime/docker-engine-probe.d.ts +83 -0
  39. package/dist/runtime/docker-engine-probe.js +300 -0
  40. package/dist/runtime/generated-output-reset.d.ts +10 -0
  41. package/dist/runtime/generated-output-reset.js +103 -0
  42. package/dist/runtime/host-diagnostics.d.ts +4 -0
  43. package/dist/runtime/host-diagnostics.js +95 -0
  44. package/dist/runtime/host-listener-inspector.d.ts +46 -0
  45. package/dist/runtime/host-listener-inspector.js +349 -0
  46. package/dist/runtime/host-port-broker.d.ts +76 -0
  47. package/dist/runtime/host-port-broker.js +333 -0
  48. package/dist/runtime/host-runtime-contract.d.ts +56 -0
  49. package/dist/runtime/host-runtime-contract.js +709 -0
  50. package/dist/runtime/host-runtime-lease.d.ts +201 -0
  51. package/dist/runtime/host-runtime-lease.js +2314 -0
  52. package/dist/runtime/host-runtime-promotion.d.ts +32 -0
  53. package/dist/runtime/host-runtime-promotion.js +206 -0
  54. package/dist/runtime/memory-capacity.d.ts +68 -0
  55. package/dist/runtime/memory-capacity.js +121 -0
  56. package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
  57. package/dist/runtime/node-host-runtime-adapter.js +284 -0
  58. package/dist/runtime/pinned-directory-process.d.ts +4 -0
  59. package/dist/runtime/pinned-directory-process.js +20 -0
  60. package/dist/runtime/pinned-source-directory.d.ts +9 -0
  61. package/dist/runtime/pinned-source-directory.js +56 -0
  62. package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
  63. package/dist/runtime/playwright-host-runtime-adapter.js +268 -0
  64. package/dist/runtime/prepared-runtime-identity.d.ts +3 -0
  65. package/dist/runtime/prepared-runtime-identity.js +191 -0
  66. package/dist/runtime/prepared-runtime-readiness.d.ts +22 -0
  67. package/dist/runtime/prepared-runtime-readiness.js +191 -0
  68. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  69. package/dist/runtime/runtime-isolation-lease.js +158 -0
  70. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  71. package/dist/runtime/workspace-resource-estimator.js +174 -0
  72. package/dist/sandbox/command-runner.d.ts +4 -0
  73. package/dist/sandbox/command-runner.js +47 -0
  74. package/dist/sandbox/contract.d.ts +5 -0
  75. package/dist/sandbox/contract.js +276 -0
  76. package/dist/sandbox/event-budget.d.ts +11 -0
  77. package/dist/sandbox/event-budget.js +27 -0
  78. package/dist/sandbox/image-policy.d.ts +35 -0
  79. package/dist/sandbox/image-policy.js +247 -0
  80. package/dist/sandbox/index.d.ts +5 -0
  81. package/dist/sandbox/index.js +5 -0
  82. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  83. package/dist/sandbox/lease-watchdog.js +67 -0
  84. package/dist/sandbox/preflight.d.ts +84 -0
  85. package/dist/sandbox/preflight.js +521 -0
  86. package/dist/sandbox/process.d.ts +20 -0
  87. package/dist/sandbox/process.js +209 -0
  88. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  89. package/dist/sandbox/provider-auth/codex.js +212 -0
  90. package/dist/sandbox/repository-lock.d.ts +5 -0
  91. package/dist/sandbox/repository-lock.js +121 -0
  92. package/dist/sandbox/runtime.d.ts +297 -0
  93. package/dist/sandbox/runtime.js +2338 -0
  94. package/dist/sandbox/sandbox.types.d.ts +93 -0
  95. package/dist/sandbox/sandbox.types.js +1 -0
  96. package/dist/sandbox/smoke.d.ts +76 -0
  97. package/dist/sandbox/smoke.js +380 -0
  98. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  99. package/dist/sandbox/worker-supervisor.js +36 -0
  100. package/dist/sandbox/worker.d.ts +1 -0
  101. package/dist/sandbox/worker.js +225 -0
  102. package/dist/sandbox/workspace.service.d.ts +13 -0
  103. package/dist/sandbox/workspace.service.js +72 -0
  104. package/dist/session/managed-attempt-tools.d.ts +21 -0
  105. package/dist/session/managed-attempt-tools.js +912 -0
  106. package/dist/session/managed-build-coordinator.d.ts +5 -0
  107. package/dist/session/managed-build-coordinator.js +105 -0
  108. package/dist/session/managed-build-receipt.d.ts +21 -0
  109. package/dist/session/managed-build-receipt.js +335 -0
  110. package/dist/session/phase-completion.artifacts.d.ts +17 -0
  111. package/dist/session/phase-completion.artifacts.js +256 -26
  112. package/dist/session/phase-completion.d.ts +12 -2
  113. package/dist/session/phase-completion.helpers.d.ts +5 -0
  114. package/dist/session/phase-completion.helpers.js +51 -10
  115. package/dist/session/phase-completion.js +14 -3
  116. package/dist/session/phase-completion.prompts.d.ts +3 -3
  117. package/dist/session/phase-completion.prompts.js +24 -8
  118. package/dist/session/phase-completion.runtime.d.ts +13 -1
  119. package/dist/session/phase-completion.runtime.js +189 -8
  120. package/dist/session/phase-completion.schemas.js +117 -4
  121. package/dist/session/phase-completion.validation.d.ts +4 -1
  122. package/dist/session/phase-completion.validation.js +153 -22
  123. package/dist/session/pre-publication-validation.d.ts +47 -0
  124. package/dist/session/pre-publication-validation.js +249 -0
  125. package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
  126. package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
  127. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
  128. package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
  129. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  130. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
  131. package/dist/session/provider-cli/provider-cli.deterministic.js +68 -9
  132. package/dist/session/provider-cli/provider-cli.process.d.ts +26 -0
  133. package/dist/session/provider-cli/provider-cli.process.js +474 -23
  134. package/dist/session/provider-cli/provider-cli.prompts.d.ts +14 -2
  135. package/dist/session/provider-cli/provider-cli.prompts.js +147 -25
  136. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
  137. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  138. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  139. package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
  140. package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
  141. package/dist/session/provider-cli/provider-cli.service.d.ts +25 -2
  142. package/dist/session/provider-cli/provider-cli.service.js +636 -84
  143. package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
  144. package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
  145. package/dist/session/provider-cli/provider-output-state.js +91 -16
  146. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  147. package/dist/session/provider-cli/provider-usage.js +28 -5
  148. package/dist/session/session-redaction.d.ts +3 -0
  149. package/dist/session/session-redaction.js +38 -0
  150. package/dist/session/session.base.d.ts +6 -1
  151. package/dist/session/session.service.d.ts +8 -2
  152. package/dist/session/session.service.js +531 -42
  153. package/dist/session/session.types.d.ts +115 -1
  154. package/dist/session/session.types.js +194 -3
  155. package/dist/shared/format/format-bytes.d.ts +1 -0
  156. package/dist/shared/format/format-bytes.js +9 -0
  157. package/dist/shared/git/git.js +2 -1
  158. package/dist/shared/path/path.d.ts +1 -0
  159. package/dist/shared/path/path.js +21 -1
  160. package/dist/shared/process/dotnet-toolchain.d.ts +14 -0
  161. package/dist/shared/process/dotnet-toolchain.js +179 -0
  162. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  163. package/dist/shared/process/host-process-recovery.js +78 -0
  164. package/dist/shared/process/host-process-supervisor.d.ts +59 -0
  165. package/dist/shared/process/host-process-supervisor.js +330 -0
  166. package/dist/shared/process/process.d.ts +2 -0
  167. package/dist/shared/process/process.js +11 -6
  168. package/dist/shared/provider-capabilities.d.ts +1 -0
  169. package/dist/shared/provider-capabilities.js +3 -0
  170. package/dist/shared/redaction/redaction.js +12 -2
  171. package/dist/version.d.ts +1 -1
  172. package/dist/version.js +1 -1
  173. package/dist/worktree/artifact-injector.d.ts +3 -2
  174. package/dist/worktree/artifact-injector.js +43 -4
  175. package/dist/worktree/dependency-warmer.d.ts +28 -1
  176. package/dist/worktree/dependency-warmer.js +308 -9
  177. package/dist/worktree/index.d.ts +2 -1
  178. package/dist/worktree/index.js +1 -0
  179. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  180. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  181. package/dist/worktree/worktree-paths.d.ts +4 -0
  182. package/dist/worktree/worktree-paths.js +30 -0
  183. package/dist/worktree/worktree.base.d.ts +3 -0
  184. package/dist/worktree/worktree.service.d.ts +14 -2
  185. package/dist/worktree/worktree.service.js +296 -47
  186. package/dist/worktree/worktree.types.d.ts +5 -1
  187. package/package.json +8 -4
  188. package/runtime/windows-job-supervisor.ps1 +444 -0
  189. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -0,0 +1,94 @@
1
+ import { runPinnedDirectoryProcess } from "./pinned-directory-process.js";
2
+ /** A separate process pins cwd without changing the multithreaded Bridge's cwd.
3
+ * All writes use single relative components, never a re-resolved ancestor path.
4
+ */
5
+ export async function runAttemptPathOperation(parent, identity, operation) {
6
+ try {
7
+ await runPinnedDirectoryProcess(parent, identity, worker, operation);
8
+ }
9
+ catch (error) {
10
+ throw new Error(`playwright_runtime_adapter_invalid: pinned directory operation failed: ${String(error)}`, { cause: error });
11
+ }
12
+ }
13
+ /** Archive copies may be large or numerous: never block Bridge heartbeats while a pinned worker writes. */
14
+ export async function runAttemptArchiveOperation(parent, identity, operation) {
15
+ try {
16
+ await runPinnedDirectoryProcess(parent, identity, worker, { kind: "archive", name: operation.name, sha256: operation.sha256 }, operation.bytes);
17
+ }
18
+ catch (error) {
19
+ throw new Error(`host_diagnostics_copy_failed: pinned directory operation failed: ${String(error)}`, { cause: error });
20
+ }
21
+ }
22
+ const worker = String.raw `
23
+ const fs = require('node:fs');
24
+ const path = require('node:path');
25
+ const input = fs.readFileSync(0);
26
+ const boundary = input.indexOf(10);
27
+ const job = JSON.parse(input.subarray(0, boundary).toString('utf8'));
28
+ const same = (a, b) => a.dev === b.dev && a.ino === b.ino;
29
+ const samePath = (a, b) => process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
30
+ const component = value => {
31
+ if (typeof value !== 'string' || !value || value === '.' || value === '..'
32
+ || ['/', ':', String.fromCharCode(92), String.fromCharCode(0)].some(char => value.includes(char)))
33
+ throw new Error('invalid relative component');
34
+ };
35
+ const statCwd = () => fs.statSync('.', { bigint: true });
36
+ try {
37
+ if (!same(statCwd(), { dev: BigInt(job.dev), ino: BigInt(job.ino) })
38
+ || !samePath(fs.realpathSync('.'), job.parent)) throw new Error('parent identity changed');
39
+ if (job.kind === 'mkdir' || job.kind === 'private-directory-mode') {
40
+ let expected = job.parent;
41
+ for (const name of job.segments) {
42
+ component(name);
43
+ let before;
44
+ try { before = fs.lstatSync(name, { bigint: true }); }
45
+ catch (error) {
46
+ if (error.code !== 'ENOENT' || job.kind !== 'mkdir') throw error;
47
+ try { fs.mkdirSync(name, { mode: 0o700 }); }
48
+ catch (mkdirError) { if (mkdirError.code !== 'EEXIST') throw mkdirError; }
49
+ before = fs.lstatSync(name, { bigint: true });
50
+ }
51
+ if (!before.isDirectory() || before.isSymbolicLink()) throw new Error('redirected directory');
52
+ process.chdir(name);
53
+ expected = path.join(expected, name);
54
+ if (!same(before, statCwd()) || !samePath(fs.realpathSync('.'), expected)) throw new Error('child identity changed');
55
+ }
56
+ if (job.kind === 'private-directory-mode' && process.platform !== 'win32') fs.chmodSync('.', 0o700);
57
+ } else if (job.kind === 'overlay' || job.kind === 'archive') {
58
+ component(job.name);
59
+ let before;
60
+ try { before = fs.lstatSync(job.name, { bigint: true }); }
61
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
62
+ if (before && (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1n))
63
+ throw new Error('overlay must be a regular file');
64
+ const fd = fs.openSync(job.name, (job.kind === 'archive' ? fs.constants.O_RDWR : fs.constants.O_WRONLY) | (fs.constants.O_NOFOLLOW ?? 0)
65
+ | (fs.constants.O_NONBLOCK ?? 0) | (before ? 0 : fs.constants.O_CREAT | fs.constants.O_EXCL), 0o600);
66
+ try {
67
+ const opened = fs.fstatSync(fd, { bigint: true });
68
+ if (!opened.isFile() || opened.nlink !== 1n || (before && !same(before, opened)))
69
+ throw new Error('overlay identity changed');
70
+ fs.ftruncateSync(fd, 0);
71
+ if (job.kind === 'overlay') fs.writeFileSync(fd, job.content, 'utf8');
72
+ else {
73
+ const bytes = input.subarray(boundary + 1);
74
+ fs.writeFileSync(fd, bytes);
75
+ fs.fsyncSync(fd);
76
+ const hash = require('node:crypto').createHash('sha256');
77
+ const chunk = Buffer.alloc(64 * 1024);
78
+ let offset = 0;
79
+ while (offset < bytes.length) {
80
+ const count = fs.readSync(fd, chunk, 0, Math.min(chunk.length, bytes.length - offset), offset);
81
+ if (!count) throw new Error('incomplete archive read');
82
+ hash.update(chunk.subarray(0, count));
83
+ offset += count;
84
+ }
85
+ const after = fs.fstatSync(fd, { bigint: true });
86
+ const current = fs.lstatSync(job.name, { bigint: true });
87
+ if (after.size !== BigInt(bytes.length) || after.nlink !== 1n || current.isSymbolicLink()
88
+ || !same(opened, after) || !same(after, current) || hash.digest('hex') !== job.sha256)
89
+ throw new Error('archive verification failed');
90
+ }
91
+ } finally { fs.closeSync(fd); }
92
+ } else throw new Error('invalid operation');
93
+ } catch (error) { process.stderr.write(error.message); process.exitCode = 1; }
94
+ `;
@@ -0,0 +1,6 @@
1
+ import type { BigIntStats } from "node:fs";
2
+ /** Validate every existing ancestor before creating anything through a pathname. */
3
+ export declare function assertCanonicalDirectory(target: string, allowMissing?: boolean): Promise<BigIntStats | undefined>;
4
+ export declare function createCanonicalDirectory(target: string): Promise<void>;
5
+ export declare function createPrivateCanonicalDirectory(target: string): Promise<void>;
6
+ export declare function restorePrivateDirectoryMode(target: string): Promise<void>;
@@ -0,0 +1,54 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { runAttemptPathOperation } from "./attempt-path-operation.js";
4
+ /** Validate every existing ancestor before creating anything through a pathname. */
5
+ export async function assertCanonicalDirectory(target, allowMissing = false) {
6
+ const resolved = path.resolve(target);
7
+ const parent = path.dirname(resolved);
8
+ if (parent !== resolved)
9
+ await assertCanonicalDirectory(parent, allowMissing);
10
+ try {
11
+ const stat = await fs.lstat(resolved, { bigint: true });
12
+ const real = await fs.realpath(resolved);
13
+ const realStat = await fs.lstat(real, { bigint: true });
14
+ if (stat.isSymbolicLink() || !stat.isDirectory() || !samePath(real, resolved)
15
+ || stat.dev !== realStat.dev || stat.ino !== realStat.ino) {
16
+ throw new Error("playwright_runtime_adapter_invalid: writable paths must be canonical, link-free directories.");
17
+ }
18
+ return realStat;
19
+ }
20
+ catch (error) {
21
+ if (allowMissing && error.code === "ENOENT")
22
+ return;
23
+ throw error;
24
+ }
25
+ }
26
+ export async function createCanonicalDirectory(target) {
27
+ if (await assertCanonicalDirectory(target, true))
28
+ return;
29
+ const resolved = path.resolve(target);
30
+ let parent = path.dirname(resolved);
31
+ let identity = await assertCanonicalDirectory(parent, true);
32
+ while (!identity) {
33
+ parent = path.dirname(parent);
34
+ identity = await assertCanonicalDirectory(parent, true);
35
+ }
36
+ await runAttemptPathOperation(parent, identity, { kind: "mkdir", segments: path.relative(parent, resolved).split(path.sep) });
37
+ await assertCanonicalDirectory(resolved);
38
+ }
39
+ export async function createPrivateCanonicalDirectory(target) {
40
+ await createCanonicalDirectory(target);
41
+ await restorePrivateDirectoryMode(target);
42
+ }
43
+ export async function restorePrivateDirectoryMode(target) {
44
+ if (process.platform === "win32")
45
+ return;
46
+ const resolved = path.resolve(target);
47
+ const parent = path.dirname(resolved);
48
+ const identity = await assertCanonicalDirectory(parent);
49
+ await runAttemptPathOperation(parent, identity, { kind: "private-directory-mode", segments: [path.basename(resolved)] });
50
+ await assertCanonicalDirectory(resolved);
51
+ }
52
+ function samePath(left, right) {
53
+ return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
54
+ }
@@ -0,0 +1,20 @@
1
+ import type { ProviderProcessHandlers, ProviderProcessRunRequest, ProviderProcessRunner, ProviderProcessRunResult } from "../session/provider-cli/provider-cli.process.js";
2
+ import { AttemptProcessSupervisor, type AttemptProcessSupervisorIdentity, type AttemptProcessSupervisorOptions } from "./attempt-process-supervisor.js";
3
+ /** Runner-local authority for active host attempt process coordinators. */
4
+ export declare class AttemptProcessSupervisorRegistry {
5
+ private readonly active;
6
+ register(identity: AttemptProcessSupervisorIdentity, runner: ProviderProcessRunner, options?: AttemptProcessSupervisorOptions): AttemptProcessSupervisor;
7
+ require(dispatchAttemptId: string, fencingToken: string): AttemptProcessSupervisor;
8
+ unregister(dispatchAttemptId: string, fencingToken: string): boolean;
9
+ has(dispatchAttemptId: string): boolean;
10
+ }
11
+ /**
12
+ * Routes managed-host provider commands through the attempt's named provider
13
+ * child while retaining the raw runner for Docker and unmanaged execution.
14
+ */
15
+ export declare class AttemptCoordinatedProviderProcessRunner implements ProviderProcessRunner {
16
+ private readonly registry;
17
+ private readonly rawRunner;
18
+ constructor(registry: AttemptProcessSupervisorRegistry, rawRunner: ProviderProcessRunner);
19
+ run(request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<ProviderProcessRunResult>;
20
+ }
@@ -0,0 +1,64 @@
1
+ import { AttemptProcessSupervisor, } from "./attempt-process-supervisor.js";
2
+ /** Runner-local authority for active host attempt process coordinators. */
3
+ export class AttemptProcessSupervisorRegistry {
4
+ active = new Map();
5
+ register(identity, runner, options = {}) {
6
+ const existing = this.active.get(identity.dispatchAttemptId);
7
+ if (existing) {
8
+ if (existing.fencingToken !== identity.fencingToken) {
9
+ throw new Error("attempt_process_registry_conflict: dispatch attempt is registered under a different fence.");
10
+ }
11
+ return existing.supervisor;
12
+ }
13
+ const supervisor = new AttemptProcessSupervisor(identity, runner, options);
14
+ this.active.set(identity.dispatchAttemptId, { fencingToken: identity.fencingToken, supervisor });
15
+ return supervisor;
16
+ }
17
+ require(dispatchAttemptId, fencingToken) {
18
+ const registered = this.active.get(dispatchAttemptId);
19
+ if (!registered) {
20
+ throw new Error("attempt_process_registry_missing: no process coordinator owns this dispatch attempt.");
21
+ }
22
+ if (registered.fencingToken !== fencingToken) {
23
+ throw new Error("attempt_process_registry_fenced: process coordinator fence does not match the dispatch.");
24
+ }
25
+ return registered.supervisor;
26
+ }
27
+ unregister(dispatchAttemptId, fencingToken) {
28
+ const registered = this.active.get(dispatchAttemptId);
29
+ if (!registered || registered.fencingToken !== fencingToken)
30
+ return false;
31
+ return this.active.delete(dispatchAttemptId);
32
+ }
33
+ has(dispatchAttemptId) {
34
+ return this.active.has(dispatchAttemptId);
35
+ }
36
+ }
37
+ /**
38
+ * Routes managed-host provider commands through the attempt's named provider
39
+ * child while retaining the raw runner for Docker and unmanaged execution.
40
+ */
41
+ export class AttemptCoordinatedProviderProcessRunner {
42
+ registry;
43
+ rawRunner;
44
+ constructor(registry, rawRunner) {
45
+ this.registry = registry;
46
+ this.rawRunner = rawRunner;
47
+ }
48
+ async run(request, handlers, signal) {
49
+ if (request.env?.FORGE_RUNTIME_MODE !== "host") {
50
+ return await this.rawRunner.run(request, handlers, signal);
51
+ }
52
+ const dispatchAttemptId = requiredEnvironment(request, "FORGE_DISPATCH_ATTEMPT_ID");
53
+ const fencingToken = requiredEnvironment(request, "FORGE_FENCING_TOKEN");
54
+ const supervisor = this.registry.require(dispatchAttemptId, fencingToken);
55
+ const child = await supervisor.startChildReady("provider", request, handlers, signal, { maxStartAttempts: request.stdin === undefined ? 1 : 3, retryDelayMs: 100 });
56
+ return await child.result;
57
+ }
58
+ }
59
+ function requiredEnvironment(request, name) {
60
+ const value = request.env?.[name]?.trim();
61
+ if (!value)
62
+ throw new Error(`attempt_process_registry_invalid: ${name} is required for managed-host execution.`);
63
+ return value;
64
+ }
@@ -0,0 +1,97 @@
1
+ import type { ProviderProcessHandlers, ProviderProcessRunRequest, ProviderProcessRunner, ProviderProcessRunResult } from "../session/provider-cli/provider-cli.process.js";
2
+ export type AttemptProcessChildStatus = "starting" | "running" | "stopping" | "exited" | "failed";
3
+ export type AttemptProcessSupervisorStatus = "active" | "stopping" | "stopped" | "degraded";
4
+ export interface AttemptProcessSupervisorIdentity {
5
+ readonly processSupervisorId: string;
6
+ readonly dispatchAttemptId: string;
7
+ readonly executionLeaseId: string;
8
+ readonly fencingToken: string;
9
+ readonly logDirectory: string;
10
+ }
11
+ export interface AttemptProcessChildLedgerEntry {
12
+ readonly name: string;
13
+ readonly generation: number;
14
+ readonly childId: string;
15
+ readonly processSupervisorId: string;
16
+ readonly metadataPath: string;
17
+ readonly status: AttemptProcessChildStatus;
18
+ readonly startedAt: string;
19
+ readonly settledAt?: string;
20
+ readonly exitCode?: number;
21
+ readonly failure?: string;
22
+ readonly peakMemoryBytes?: number;
23
+ readonly memorySampleCount?: number;
24
+ readonly memorySource?: string;
25
+ readonly memoryObservedAt?: string;
26
+ }
27
+ export interface AttemptProcessSupervisorLedger {
28
+ readonly schemaVersion: 1;
29
+ readonly attemptSupervisorId: string;
30
+ readonly dispatchAttemptId: string;
31
+ readonly executionLeaseId: string;
32
+ readonly status: AttemptProcessSupervisorStatus;
33
+ readonly updatedAt: string;
34
+ readonly children: readonly AttemptProcessChildLedgerEntry[];
35
+ }
36
+ export interface AttemptProcessChildHandle {
37
+ readonly name: string;
38
+ readonly generation: number;
39
+ readonly childId: string;
40
+ readonly processSupervisorId: string;
41
+ readonly metadataPath: string;
42
+ readonly ready: Promise<void>;
43
+ readonly result: Promise<ProviderProcessRunResult>;
44
+ stop(fencingToken: string): Promise<ProviderProcessRunResult>;
45
+ }
46
+ export interface AttemptProcessSupervisorOptions {
47
+ readonly metadataReadyTimeoutMs?: number;
48
+ readonly signal?: AbortSignal;
49
+ }
50
+ export interface AttemptProcessChildReadyOptions {
51
+ readonly maxStartAttempts?: number;
52
+ readonly retryDelayMs?: number;
53
+ }
54
+ /**
55
+ * Owns the independently supervised process trees that belong to one host
56
+ * dispatch attempt. Each named child gets a distinct Windows Job Object or
57
+ * Unix process group through the existing process runner, while this
58
+ * coordinator supplies shared fencing, cancellation, and a durable ledger.
59
+ */
60
+ export declare class AttemptProcessSupervisor {
61
+ private readonly runner;
62
+ readonly ledgerPath: string;
63
+ private readonly identity;
64
+ private readonly metadataReadyTimeoutMs;
65
+ private readonly records;
66
+ private readonly currentByName;
67
+ private readonly attemptController;
68
+ private status;
69
+ private persistTail;
70
+ private persistenceFailure;
71
+ private stopOperation;
72
+ constructor(identity: AttemptProcessSupervisorIdentity, runner: ProviderProcessRunner, options?: AttemptProcessSupervisorOptions);
73
+ startChild(name: string, request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal): Promise<AttemptProcessChildHandle>;
74
+ /**
75
+ * Starts a child and proves that the OS process tree is supervised before
76
+ * returning it to the caller. A launch that settles before producing its
77
+ * provider-ready supervision record is safe to retry: the raw runner only
78
+ * persists that record after the actual provider child has started, before
79
+ * Forge announces the provider as active.
80
+ */
81
+ startChildReady(name: string, request: ProviderProcessRunRequest, handlers?: ProviderProcessHandlers, signal?: AbortSignal, options?: AttemptProcessChildReadyOptions): Promise<AttemptProcessChildHandle>;
82
+ stopChild(name: string, generation: number, fencingToken: string, reason?: unknown): Promise<ProviderProcessRunResult>;
83
+ stopAll(fencingToken: string, reason?: unknown): Promise<void>;
84
+ private performStopAll;
85
+ snapshot(): AttemptProcessSupervisorLedger;
86
+ providerMemoryTelemetry(): AttemptProcessTreeMemoryTelemetry | null;
87
+ private observeChildReady;
88
+ private settleChild;
89
+ private assertFence;
90
+ private persist;
91
+ }
92
+ export interface AttemptProcessTreeMemoryTelemetry {
93
+ readonly peakMemoryBytes: number;
94
+ readonly sampleCount: number;
95
+ readonly source: string;
96
+ readonly observedAt: string;
97
+ }