machine-bridge-mcp 3.0.0-beta.21 → 3.0.0-beta.26

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 (102) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/CONTRIBUTING.md +3 -3
  3. package/GOVERNANCE.md +2 -2
  4. package/README.md +24 -6
  5. package/browser-extension/manifest.json +1 -1
  6. package/docs/AGENT_CONTEXT.md +10 -7
  7. package/docs/ARCHITECTURE.md +35 -22
  8. package/docs/AUDIT.md +85 -1
  9. package/docs/CLIENTS.md +6 -2
  10. package/docs/ENGINEERING.md +31 -9
  11. package/docs/LOCAL_AUTOMATION.md +4 -2
  12. package/docs/LOGGING.md +8 -8
  13. package/docs/OPERATIONS.md +43 -17
  14. package/docs/PRIVACY.md +18 -4
  15. package/docs/PROJECT_STANDARDS.md +2 -2
  16. package/docs/RELEASING.md +35 -11
  17. package/docs/TESTING.md +36 -16
  18. package/docs/THREAT_MODEL.md +20 -5
  19. package/docs/TOOL_REFERENCE.md +18 -12
  20. package/docs/UPGRADING.md +32 -0
  21. package/package.json +15 -6
  22. package/scripts/check-plan.mjs +8 -0
  23. package/scripts/coverage-check.mjs +30 -1
  24. package/scripts/foreground-daemon-recovery.mjs +88 -0
  25. package/scripts/github-release.mjs +22 -16
  26. package/scripts/install-published-prerelease.mjs +7 -7
  27. package/scripts/official-mcp-conformance.mjs +243 -0
  28. package/scripts/persistent-activation-process.mjs +36 -0
  29. package/scripts/release-candidate-manifest.mjs +12 -0
  30. package/scripts/release-publication-guard.mjs +65 -0
  31. package/scripts/release-state.mjs +1 -1
  32. package/scripts/sbom-check.mjs +99 -0
  33. package/scripts/start-release-candidate.mjs +39 -13
  34. package/src/local/agent-context-projection.mjs +26 -7
  35. package/src/local/agent-context.mjs +25 -4
  36. package/src/local/autostart-log-maintenance.mjs +36 -0
  37. package/src/local/capability-observer.mjs +5 -0
  38. package/src/local/child-process-settlement.mjs +103 -0
  39. package/src/local/cli-activate.mjs +42 -5
  40. package/src/local/cli-service.mjs +55 -5
  41. package/src/local/cli.mjs +59 -10
  42. package/src/local/daemon-process.mjs +24 -3
  43. package/src/local/delegated-process-sandbox.mjs +1 -0
  44. package/src/local/execution-routing.mjs +231 -0
  45. package/src/local/git-service.mjs +3 -1
  46. package/src/local/job-runner.mjs +55 -19
  47. package/src/local/macos-trust-broker.mjs +7 -0
  48. package/src/local/managed-job-runner-claim.mjs +54 -0
  49. package/src/local/managed-job-runner.mjs +13 -2
  50. package/src/local/process-execution.mjs +2 -2
  51. package/src/local/process-identity.mjs +11 -0
  52. package/src/local/process-tree-ownership-types.d.ts +37 -0
  53. package/src/local/process-tree-ownership.mjs +49 -41
  54. package/src/local/process-tree.mjs +1 -1
  55. package/src/local/relay-call-recovery.mjs +40 -21
  56. package/src/local/runtime-activation.mjs +357 -38
  57. package/src/local/runtime-capabilities.mjs +22 -6
  58. package/src/local/runtime-diagnostics.mjs +9 -2
  59. package/src/local/runtime.mjs +18 -4
  60. package/src/local/service-convergence.mjs +33 -0
  61. package/src/local/service-owner.mjs +147 -0
  62. package/src/local/service-restart-handoff.mjs +22 -8
  63. package/src/local/service-runtime.mjs +145 -0
  64. package/src/local/service.mjs +143 -25
  65. package/src/local/state.mjs +104 -7
  66. package/src/local/stdio.mjs +139 -45
  67. package/src/local/system-network-route.mjs +76 -0
  68. package/src/local/tool-executor.mjs +24 -6
  69. package/src/local/tools.mjs +6 -5
  70. package/src/local/windows-service-convergence.mjs +49 -0
  71. package/src/local/windows-service.mjs +30 -53
  72. package/src/shared/mcp-protocol.d.mts +27 -0
  73. package/src/shared/mcp-protocol.mjs +256 -0
  74. package/src/shared/mcp-subscriptions.d.mts +4 -0
  75. package/src/shared/mcp-subscriptions.mjs +59 -0
  76. package/src/shared/relay-contract.json +1 -0
  77. package/src/shared/result-projection.d.mts +2 -1
  78. package/src/shared/result-projection.mjs +13 -2
  79. package/src/shared/server-metadata.json +11 -4
  80. package/src/shared/tool-argument-validation.d.mts +17 -0
  81. package/src/shared/tool-argument-validation.mjs +325 -0
  82. package/src/shared/tool-catalog.json +18 -12
  83. package/src/worker/durable-stream-calls.ts +12 -24
  84. package/src/worker/http.ts +36 -2
  85. package/src/worker/index.ts +181 -165
  86. package/src/worker/mcp-http-contract.ts +276 -0
  87. package/src/worker/mcp-jsonrpc.ts +12 -6
  88. package/src/worker/mcp-legacy-dispatch.ts +104 -0
  89. package/src/worker/mcp-modern-controller.ts +199 -0
  90. package/src/worker/mcp-modern-proxy.ts +126 -0
  91. package/src/worker/mcp-modern-stream.ts +71 -0
  92. package/src/worker/mcp-session.ts +12 -3
  93. package/src/worker/mcp-stream-proxy-contract.ts +67 -0
  94. package/src/worker/mcp-stream-proxy.ts +17 -60
  95. package/src/worker/mcp-tool-call-input.ts +23 -0
  96. package/src/worker/tool-catalog.ts +29 -1
  97. package/src/worker/tool-timeout.ts +53 -12
  98. package/src/worker/worker-mcp-config.ts +23 -0
  99. package/src/worker/worker-metadata.ts +10 -1
  100. package/src/worker/worker-runtime-config.ts +19 -0
  101. package/src/worker/worker-static-routes.ts +7 -2
  102. package/tsconfig.local.json +7 -1
@@ -5,6 +5,7 @@ import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
8
+ const CRITICAL_SCRIPT_FILES = new Set(["scripts/release-publication-guard.mjs"]);
8
9
  const coverageDir = mkdtempSync(resolve(tmpdir(), "machine-bridge-coverage-"));
9
10
  const tests = [
10
11
  "tests/policy-test.mjs",
@@ -12,12 +13,19 @@ const tests = [
12
13
  "tests/runtime-boundaries-test.mjs",
13
14
  "tests/worker-runtime-infrastructure-test.mjs",
14
15
  "tests/mcp-resumption-test.mjs",
16
+ "tests/mcp-protocol-test.mjs",
17
+ "tests/mcp-modern-controller-test.mjs",
18
+ "tests/tool-argument-validation-test.mjs",
15
19
  "tests/worker-oauth-controller-test.mjs",
16
20
  "tests/logging-structure-test.mjs",
17
21
  "tests/runtime-handler-matrix-test.mjs",
18
22
  "tests/cli-entrypoint-test.mjs",
19
23
  "tests/cli-service-test.mjs",
20
24
  "tests/service-restart-handoff-test.mjs",
25
+ "tests/service-platform-test.mjs",
26
+ "tests/process-lock-test.mjs",
27
+ "tests/runtime-activation-test.mjs",
28
+ "tests/release-publication-guard-test.mjs",
21
29
  "tests/local-self-test.mjs",
22
30
  "tests/runtime-self-test.mjs",
23
31
  "tests/numbers-test.mjs",
@@ -28,6 +36,7 @@ const tests = [
28
36
  "tests/agent-context-test.mjs",
29
37
  "tests/agent-boundaries-test.mjs",
30
38
  "tests/capability-ranking-test.mjs",
39
+ "tests/execution-routing-test.mjs",
31
40
  "tests/browser-bridge-test.mjs",
32
41
  "tests/relay-connection-test.mjs",
33
42
  "tests/managed-jobs-test.mjs",
@@ -67,6 +76,9 @@ try {
67
76
  "src/local/security-audit-log.mjs": [85, 55],
68
77
  "src/local/delegated-process-sandbox.mjs": [80, 45],
69
78
  "src/shared/device-session-auth.mjs": [100, null],
79
+ "src/shared/mcp-protocol.mjs": [90, 70],
80
+ "src/shared/mcp-subscriptions.mjs": [95, 75],
81
+ "src/shared/tool-argument-validation.mjs": [90, 70],
70
82
  "src/local/policy.mjs": [90, 65],
71
83
  "src/local/errors.mjs": [70, 50],
72
84
  "src/local/call-registry.mjs": [85, 55],
@@ -86,6 +98,12 @@ try {
86
98
  "src/local/service-ownership.mjs": [100, 75],
87
99
  "src/local/service-restart-scheduler.mjs": [100, 70],
88
100
  "src/local/service-restart-handoff.mjs": [100, 60],
101
+ "src/local/service-owner.mjs": [100, 85],
102
+ "src/local/service-runtime.mjs": [100, 80],
103
+ "src/local/windows-service-convergence.mjs": [100, 95],
104
+ "src/local/runtime-activation.mjs": [90, 70],
105
+ "scripts/release-publication-guard.mjs": [100, 80],
106
+ "src/local/child-process-settlement.mjs": [100, 85],
89
107
  "src/local/cli-options.mjs": [65, 35],
90
108
  "src/local/cli-policy.mjs": [70, 35],
91
109
  "src/local/numbers.mjs": [100, 100],
@@ -100,10 +118,12 @@ try {
100
118
  "src/local/agent-skill-discovery.mjs": [85, 60],
101
119
  "src/local/agent-text-file.mjs": [90, 60],
102
120
  "src/local/capability-ranking.mjs": [95, 70],
121
+ "src/local/execution-routing.mjs": [95, 70],
103
122
  "src/local/browser-extension-protocol.mjs": [95, 35],
104
123
  "src/local/browser-operation-service.mjs": [80, 50],
105
124
  "src/local/runtime-reporting.mjs": [95, 75],
106
125
  "src/local/runtime-diagnostics.mjs": [75, 65],
126
+ "src/local/system-network-route.mjs": [90, 75],
107
127
  "src/local/runtime-capabilities.mjs": [75, 45],
108
128
  "src/local/monotonic-deadline.mjs": [100, 100],
109
129
  "src/local/path-inspection.mjs": [100, 60],
@@ -112,12 +132,15 @@ try {
112
132
  "src/local/managed-jobs.mjs": [85, 50],
113
133
  "src/local/managed-job-projection.mjs": [90, 60],
114
134
  "src/local/managed-job-storage.mjs": [75, 50],
135
+ "src/local/managed-job-runner-claim.mjs": [90, 60],
115
136
  "src/local/managed-job-runner.mjs": [80, 50],
116
137
  "src/local/browser-bridge.mjs": [80, 55],
117
138
  "src/local/browser-request-registry.mjs": [95, 35],
118
139
  "src/local/browser-broker-routes.mjs": [85, 55],
119
140
  "src/local/browser-broker-server.mjs": [80, 50],
120
141
  "src/worker/account-admin.ts": [70, 35],
142
+ "src/worker/tool-timeout.ts": [95, 85],
143
+ "src/worker/tool-catalog.ts": [95, 80],
121
144
  "src/worker/daemon-auth.ts": [85, 45],
122
145
  "src/worker/dpop.ts": [90, 30],
123
146
  "src/worker/nonce-store.ts": [85, 50],
@@ -130,6 +153,12 @@ try {
130
153
  "src/worker/policy.ts": [100, 25],
131
154
  "src/worker/errors.ts": [100, 40],
132
155
  "src/worker/mcp-jsonrpc.ts": [95, 55],
156
+ "src/worker/mcp-tool-call-input.ts": [100, 75],
157
+ "src/worker/mcp-http-contract.ts": [90, 70],
158
+ "src/worker/mcp-modern-controller.ts": [90, 70],
159
+ "src/worker/mcp-modern-proxy.ts": [90, 70],
160
+ "src/worker/mcp-modern-stream.ts": [100, null],
161
+ "src/worker/worker-mcp-config.ts": [100, 75],
133
162
  "src/worker/mcp-resumption-config.ts": [100, 80],
134
163
  "src/worker/mcp-resumption-records.ts": [90, 65],
135
164
  "src/worker/mcp-stream-proxy.ts": [85, 55],
@@ -175,7 +204,7 @@ function collectCoverage(directory) {
175
204
  const repositoryRelative = relative(root, absolute);
176
205
  if (!repositoryRelative || repositoryRelative === ".." || repositoryRelative.startsWith(`..${sep}`) || isAbsolute(repositoryRelative)) continue;
177
206
  const file = repositoryRelative.split(sep).join("/");
178
- if (!file.startsWith("src/")) continue;
207
+ if (!file.startsWith("src/") && !CRITICAL_SCRIPT_FILES.has(file)) continue;
179
208
  let entry = scripts.get(file);
180
209
  if (!entry) {
181
210
  entry = { functions: new Map(), blocks: new Map() };
@@ -0,0 +1,88 @@
1
+ import { readFileSync, realpathSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { inspectProcessInstance, processCommandLine, splitProcessCommandLine } from "../src/local/process-identity.mjs";
4
+ import {
5
+ daemonLockPathForState, loadState, readDaemonLockOwner, resolveWorkspace, selectedWorkspace,
6
+ } from "../src/local/state.mjs";
7
+
8
+ const FOREGROUND_PID = /foreground daemon is active \(pid (\d+)\)/i;
9
+
10
+ export function discoverForegroundDaemonRecovery({ output, stateRoot, workspace = "", dependencies = {} } = {}) {
11
+ const pid = foregroundPid(output);
12
+ if (!pid || typeof stateRoot !== "string" || !stateRoot) return null;
13
+ const selectWorkspace = dependencies.selectedWorkspace || selectedWorkspace;
14
+ const resolve = dependencies.resolveWorkspace || resolveWorkspace;
15
+ const load = dependencies.loadState || loadState;
16
+ const readOwner = dependencies.readDaemonOwner || readDaemonLockOwner;
17
+ const lockPath = dependencies.daemonLockPathForState || daemonLockPathForState;
18
+ const inspect = dependencies.inspectProcessInstance || inspectProcessInstance;
19
+ const readCommand = dependencies.processCommandLine || processCommandLine;
20
+ const splitCommand = dependencies.splitProcessCommandLine || splitProcessCommandLine;
21
+ const canonical = dependencies.realpathSync || realpathSync;
22
+ const fileInfo = dependencies.statSync || statSync;
23
+ const readFile = dependencies.readFileSync || readFileSync;
24
+ let targetWorkspace;
25
+ let canonicalStateRoot;
26
+ let state;
27
+ let owner;
28
+ try {
29
+ targetWorkspace = resolve(workspace || selectWorkspace(stateRoot));
30
+ canonicalStateRoot = canonical(path.resolve(stateRoot));
31
+ state = load(targetWorkspace, { stateDir: canonicalStateRoot });
32
+ owner = readOwner(lockPath(state));
33
+ } catch {
34
+ return null;
35
+ }
36
+ if (Number(owner?.pid) !== pid || owner?.mode !== "foreground" || owner?.purpose !== "daemon") return null;
37
+ if (inspect(owner)?.current !== true) return null;
38
+ if (!sameCanonical(owner.workspace, targetWorkspace, canonical)) return null;
39
+ let entry;
40
+ let argv;
41
+ try {
42
+ entry = canonical(String(owner.entryScript || ""));
43
+ if (!fileInfo(entry).isFile()) return null;
44
+ argv = splitCommand(readCommand(pid));
45
+ } catch {
46
+ return null;
47
+ }
48
+ const entryIndex = argv.findIndex(value => sameCanonical(value, entry, canonical));
49
+ if (entryIndex < 0 || argv[entryIndex + 1] !== "start" || argv.includes("--daemon-only")) return null;
50
+ const commandWorkspace = commandFlagValue(argv, "--workspace");
51
+ const commandStateRoot = commandFlagValue(argv, "--state-dir");
52
+ if (!sameCanonical(commandWorkspace, targetWorkspace, canonical)
53
+ || !sameCanonical(commandStateRoot, canonicalStateRoot, canonical)) return null;
54
+ const packageRoot = path.dirname(path.dirname(entry));
55
+ let pkg;
56
+ try {
57
+ pkg = JSON.parse(readFile(path.join(packageRoot, "package.json"), "utf8"));
58
+ } catch {
59
+ return null;
60
+ }
61
+ if (pkg?.name !== "machine-bridge-mcp" || pkg.version !== owner.version) return null;
62
+ if (!["machine-mcp", "machine-mcp.mjs"].includes(path.basename(entry))) return null;
63
+ return { pid, cli: entry, version: owner.version, workspace: targetWorkspace, stateRoot: canonicalStateRoot };
64
+ }
65
+
66
+ export function foregroundPid(output) {
67
+ const match = FOREGROUND_PID.exec(String(output || ""));
68
+ const pid = Number(match?.[1]);
69
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : 0;
70
+ }
71
+
72
+ function commandFlagValue(argv, name) {
73
+ const exact = argv.find(value => value.startsWith(`${name}=`));
74
+ if (exact) return exact.slice(name.length + 1);
75
+ const index = argv.indexOf(name);
76
+ return index >= 0 && index + 1 < argv.length ? argv[index + 1] : "";
77
+ }
78
+
79
+ function sameCanonical(left, right, canonical) {
80
+ if (typeof left !== "string" || typeof right !== "string" || !left || !right) return false;
81
+ try {
82
+ const a = canonical(path.resolve(left));
83
+ const b = canonical(path.resolve(right));
84
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
@@ -15,14 +15,14 @@ import { tagSyncError } from "./release-state.mjs";
15
15
  import { verifyCurrentReleaseAcceptance } from "./release-acceptance.mjs";
16
16
  import { parseReleaseVersion, requiresSoakForStable } from "./release-channel.mjs";
17
17
  import { verifyCurrentStableSoak } from "./release-soak.mjs";
18
+ import { assertOwnerTerminalPublication, withGithubPublicationLock } from "./release-publication-guard.mjs";
18
19
  import { fileURLToPath } from "node:url";
19
20
 
20
21
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
21
22
  process.chdir(root);
22
23
 
23
24
  function fail(message) {
24
- console.error(`release error: ${message}`);
25
- process.exit(1);
25
+ throw new Error(String(message || "release failed"));
26
26
  }
27
27
 
28
28
  function run(command, args, options = {}) {
@@ -304,8 +304,8 @@ function publishCurrent({ prereleaseMode = false } = {}) {
304
304
  const parsedVersion = parseReleaseVersion(pkg.version);
305
305
  if (prereleaseMode !== parsedVersion.prerelease) {
306
306
  fail(parsedVersion.prerelease
307
- ? "prerelease versions must use npm run prerelease:release"
308
- : "stable versions must use npm run release");
307
+ ? "prerelease versions must use npm run prerelease:release -- --owner-terminal-confirm"
308
+ : "stable versions must use npm run release -- --owner-terminal-confirm");
309
309
  }
310
310
  if (!parsedVersion.prerelease && requiresSoakForStable(pkg.version)) assertStableSoak();
311
311
  const tag = `v${pkg.version}`;
@@ -429,16 +429,22 @@ function backfillMissingReleases() {
429
429
  }
430
430
 
431
431
  const mode = process.argv[2] ?? "--check";
432
- if (mode === "--check") {
433
- ensureClean();
434
- fetchRemote();
435
- assertCoreSync({ requireReleaseAsset: true });
436
- } else if (mode === "--publish") {
437
- publishCurrent({ prereleaseMode: false });
438
- } else if (mode === "--publish-prerelease") {
439
- publishCurrent({ prereleaseMode: true });
440
- } else if (mode === "--backfill") {
441
- backfillMissingReleases();
442
- } else {
443
- fail("usage: node scripts/github-release.mjs [--check|--publish|--publish-prerelease|--backfill]");
432
+ try {
433
+ if (mode === "--check") {
434
+ ensureClean();
435
+ fetchRemote();
436
+ assertCoreSync({ requireReleaseAsset: true });
437
+ } else if (mode === "--publish" || mode === "--publish-prerelease" || mode === "--backfill") {
438
+ assertOwnerTerminalPublication();
439
+ await withGithubPublicationLock(root, async () => {
440
+ if (mode === "--publish") publishCurrent({ prereleaseMode: false });
441
+ else if (mode === "--publish-prerelease") publishCurrent({ prereleaseMode: true });
442
+ else backfillMissingReleases();
443
+ });
444
+ } else {
445
+ fail("usage: node scripts/github-release.mjs [--check|--publish|--publish-prerelease|--backfill] [--owner-terminal-confirm]");
446
+ }
447
+ } catch (error) {
448
+ console.error(`release error: ${String(error?.message || error)}`);
449
+ process.exitCode = 1;
444
450
  }
@@ -11,6 +11,7 @@ import { computePromotionContentDigest } from "./promotion-digest.mjs";
11
11
  import { readPublishedNpmPrerelease } from "./published-release.mjs";
12
12
  import { verifyCurrentReleaseAcceptance } from "./release-acceptance.mjs";
13
13
  import { assertSoakEligiblePrerelease } from "./release-channel.mjs";
14
+ import { persistentActivationSpawnOptions } from "./persistent-activation-process.mjs";
14
15
 
15
16
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
16
17
  const npmCli = process.env.npm_execpath;
@@ -103,13 +104,11 @@ function currentGlobalInstallation(packageName) {
103
104
  }
104
105
 
105
106
  function runActivation(entry, args) {
106
- const result = spawnSync(process.execPath, [entry, ...args], {
107
- cwd: root,
108
- env: process.env,
109
- encoding: "utf8",
110
- timeout: 300_000,
111
- windowsHide: true,
112
- });
107
+ const result = spawnSync(
108
+ process.execPath,
109
+ [entry, ...args],
110
+ persistentActivationSpawnOptions({ cwd: root, env: process.env }),
111
+ );
113
112
  if (result.error) throw result.error;
114
113
  if (result.status !== 0) throw new Error(`prerelease runtime activation failed: ${boundedDiagnostic(result.stderr || result.stdout)}`);
115
114
  try { return JSON.parse(result.stdout); } catch { throw new Error("prerelease runtime activation did not return valid JSON"); }
@@ -121,6 +120,7 @@ function runNpm(args) {
121
120
  env: process.env,
122
121
  encoding: "utf8",
123
122
  timeout: 300_000,
123
+ killSignal: "SIGKILL",
124
124
  windowsHide: true,
125
125
  });
126
126
  if (result.error) throw result.error;
@@ -0,0 +1,243 @@
1
+ import { createServer } from "node:http";
2
+ import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { runExecutable } from "../src/local/shell.mjs";
6
+
7
+ const MAX_PROXY_REQUEST_BYTES = 16 * 1024 * 1024;
8
+ const DEFAULT_TIMEOUT_MS = 60_000;
9
+
10
+ export async function runOfficialMcpConformance(options) {
11
+ const checkout = validateConformanceCheckout(options.checkout);
12
+ const upstream = validatedUpstream(options.upstream);
13
+ const accessToken = boundedSecret(options.accessToken, "access token", 16 * 1024);
14
+ const scenario = requiredText(options.scenario, "conformance scenario");
15
+ const specVersion = String(options.specVersion || "2026-07-28");
16
+ const timeoutMs = positiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS);
17
+ const proxy = await startBearerProxy({ upstream, accessToken });
18
+ try {
19
+ const npmCli = requiredText(process.env.npm_execpath, "npm_execpath");
20
+ const result = await runCommand({
21
+ command: process.execPath,
22
+ args: [
23
+ npmCli, "start", "--", "server", "--url", `${proxy.origin}/mcp`,
24
+ "--scenario", scenario, "--spec-version", specVersion,
25
+ ...(options.expectedFailures ? ["--expected-failures", String(options.expectedFailures)] : []),
26
+ ...(options.verbose === true ? ["--verbose"] : []),
27
+ ],
28
+ cwd: checkout,
29
+ timeoutMs,
30
+ });
31
+ return Object.freeze({ ...result, scenario, specVersion, proxyOrigin: proxy.origin });
32
+ } finally {
33
+ await proxy.close();
34
+ }
35
+ }
36
+
37
+ async function startBearerProxy({ upstream, accessToken }) {
38
+ const server = createServer((request, response) => {
39
+ void proxyRequest(request, response, upstream, accessToken);
40
+ });
41
+ await new Promise((resolve, reject) => {
42
+ server.once("error", reject);
43
+ server.listen(0, "127.0.0.1", resolve);
44
+ });
45
+ const address = server.address();
46
+ if (!address || typeof address === "string") throw new Error("conformance proxy did not bind a TCP port");
47
+ return {
48
+ origin: `http://127.0.0.1:${address.port}`,
49
+ close: () => new Promise((resolve, reject) => {
50
+ server.close((error) => { if (error) reject(error); else resolve(); });
51
+ server.closeAllConnections?.();
52
+ }),
53
+ };
54
+ }
55
+
56
+ async function proxyRequest(request, response, upstream, accessToken) {
57
+ const controller = new AbortController();
58
+ request.once("aborted", () => controller.abort());
59
+ response.once("close", () => { if (!response.writableEnded) controller.abort(); });
60
+ try {
61
+ const body = await readBoundedBody(request, MAX_PROXY_REQUEST_BYTES);
62
+ const headers = new Headers();
63
+ for (const [name, value] of Object.entries(request.headers)) {
64
+ if (value === undefined || ["host", "connection", "content-length", "transfer-encoding"].includes(name.toLowerCase())) continue;
65
+ if (Array.isArray(value)) for (const item of value) headers.append(name, item);
66
+ else headers.set(name, value);
67
+ }
68
+ headers.set("authorization", `Bearer ${accessToken}`);
69
+ const target = conformanceProxyTarget(request.url, upstream);
70
+ const upstreamResponse = await fetch(target, {
71
+ method: request.method,
72
+ headers,
73
+ body: body.length ? body : undefined,
74
+ signal: controller.signal,
75
+ redirect: "manual",
76
+ });
77
+ const responseHeaders = {};
78
+ upstreamResponse.headers.forEach((value, name) => {
79
+ if (!["connection", "content-encoding", "content-length", "transfer-encoding"].includes(name.toLowerCase())) responseHeaders[name] = value;
80
+ });
81
+ response.writeHead(upstreamResponse.status, responseHeaders);
82
+ if (!upstreamResponse.body) return response.end();
83
+ const reader = upstreamResponse.body.getReader();
84
+ try {
85
+ for (;;) {
86
+ const { done, value } = await reader.read();
87
+ if (done) break;
88
+ if (value && !response.write(Buffer.from(value))) {
89
+ await new Promise((resolve) => { response.once("drain", resolve); });
90
+ }
91
+ }
92
+ response.end();
93
+ } finally {
94
+ reader.releaseLock();
95
+ }
96
+ } catch (error) {
97
+ if (controller.signal.aborted) return response.destroy();
98
+ if (!response.headersSent) response.writeHead(error?.code === "request_too_large" ? 413 : 502, { "content-type": "application/json" });
99
+ response.end(JSON.stringify({ error: "conformance_proxy_failure" }));
100
+ }
101
+ }
102
+
103
+ function readBoundedBody(request, maximumBytes) {
104
+ return new Promise((resolve, reject) => {
105
+ const chunks = [];
106
+ let bytes = 0;
107
+ let settled = false;
108
+ const finish = (callback, value) => {
109
+ if (settled) return;
110
+ settled = true;
111
+ request.removeListener("data", onData);
112
+ request.removeListener("end", onEnd);
113
+ request.removeListener("aborted", onAborted);
114
+ request.removeListener("error", onError);
115
+ callback(value);
116
+ };
117
+ const onData = (chunk) => {
118
+ bytes += chunk.length;
119
+ if (bytes > maximumBytes) {
120
+ const error = new Error("conformance proxy request is too large");
121
+ error.code = "request_too_large";
122
+ finish(reject, error);
123
+ request.destroy();
124
+ return;
125
+ }
126
+ chunks.push(chunk);
127
+ };
128
+ const onEnd = () => finish(resolve, Buffer.concat(chunks, bytes));
129
+ const onAborted = () => finish(reject, proxyInputError("conformance proxy request was aborted"));
130
+ const onError = (error) => finish(reject, error);
131
+ request.on("data", onData);
132
+ request.once("end", onEnd);
133
+ request.once("aborted", onAborted);
134
+ request.once("error", onError);
135
+ });
136
+ }
137
+
138
+ async function runCommand({ command, args, cwd, timeoutMs }) {
139
+ const result = await runExecutable(command, args, {
140
+ cwd,
141
+ env: { ...process.env, NO_COLOR: "1", CI: "1" },
142
+ capture: true,
143
+ allowFailure: true,
144
+ timeoutMs,
145
+ maxOutputBytes: 4 * 1024 * 1024,
146
+ });
147
+ return Object.freeze({ ...result, signal: null });
148
+ }
149
+
150
+ export function validateConformanceCheckout(value) {
151
+ const requested = resolve(requiredText(value, "conformance checkout"));
152
+ if (!existsSync(requested)) throw new Error("conformance checkout does not exist");
153
+ const requestedInfo = lstatSync(requested);
154
+ if (requestedInfo.isSymbolicLink() || !requestedInfo.isDirectory()) {
155
+ throw new Error("conformance checkout must be a real directory");
156
+ }
157
+ const checkout = realpathSync.native(requested);
158
+ for (const file of ["package.json", "package-lock.json"]) {
159
+ const path = join(checkout, file);
160
+ if (!existsSync(path)) throw new Error(`conformance checkout omits ${file}`);
161
+ const info = lstatSync(path);
162
+ if (info.isSymbolicLink() || !info.isFile()) throw new Error(`conformance checkout ${file} must be a regular file`);
163
+ }
164
+ let packageState;
165
+ try { packageState = JSON.parse(readFileSync(join(checkout, "package.json"), "utf8")); }
166
+ catch { throw new Error("conformance checkout package.json is invalid"); }
167
+ if (typeof packageState?.scripts?.start !== "string" || !packageState.scripts.start.trim()) {
168
+ throw new Error("conformance checkout omits its start command");
169
+ }
170
+ const nodeModules = join(checkout, "node_modules");
171
+ if (!existsSync(nodeModules) || !lstatSync(nodeModules).isDirectory()) {
172
+ throw new Error("conformance checkout dependencies are not installed; run npm ci --ignore-scripts in the checkout");
173
+ }
174
+ return checkout;
175
+ }
176
+
177
+ export function conformanceProxyTarget(requestTarget, upstream) {
178
+ const raw = String(requestTarget || "/mcp");
179
+ if (Buffer.byteLength(raw) > 8192) throw proxyInputError("conformance proxy request target is too large");
180
+ let parsed;
181
+ try { parsed = new URL(raw, "http://proxy.invalid"); }
182
+ catch { throw proxyInputError("conformance proxy request target is invalid"); }
183
+ if (parsed.origin !== "http://proxy.invalid" || parsed.hash) {
184
+ throw proxyInputError("conformance proxy requires a relative request target");
185
+ }
186
+ if (parsed.pathname !== "/mcp") throw proxyInputError("conformance proxy accepts only its MCP endpoint");
187
+ const target = new URL(upstream.href);
188
+ if (parsed.search) target.search = parsed.search;
189
+ return target;
190
+ }
191
+
192
+ function validatedUpstream(value) {
193
+ let upstream;
194
+ try { upstream = new URL(requiredText(value, "upstream MCP URL")); }
195
+ catch { throw new Error("upstream MCP URL is invalid"); }
196
+ if (upstream.username || upstream.password || upstream.hash) throw new Error("upstream MCP URL must not contain credentials or a fragment");
197
+ const loopback = upstream.hostname === "localhost" || upstream.hostname === "127.0.0.1" || upstream.hostname === "[::1]";
198
+ if (upstream.protocol !== "https:" && !(upstream.protocol === "http:" && loopback)) {
199
+ throw new Error("upstream MCP URL must use HTTPS or loopback HTTP");
200
+ }
201
+ return upstream;
202
+ }
203
+
204
+ function boundedSecret(value, label, maximumBytes) {
205
+ const text = requiredText(value, label);
206
+ if (Buffer.byteLength(text) > maximumBytes) throw new Error(`${label} is too large`);
207
+ return text;
208
+ }
209
+
210
+ function proxyInputError(message) {
211
+ const error = new Error(message);
212
+ error.code = "invalid_proxy_input";
213
+ return error;
214
+ }
215
+
216
+ function requiredText(value, label) {
217
+ const text = String(value || "").trim();
218
+ if (!text) throw new Error(`${label} is required`);
219
+ return text;
220
+ }
221
+
222
+ function positiveInteger(value, fallback) {
223
+ const number = Number(value);
224
+ return Number.isInteger(number) && number > 0 ? number : fallback;
225
+ }
226
+
227
+ async function main() {
228
+ const result = await runOfficialMcpConformance({
229
+ checkout: process.env.MBM_OFFICIAL_CONFORMANCE_CHECKOUT,
230
+ upstream: process.env.MBM_OFFICIAL_CONFORMANCE_UPSTREAM,
231
+ accessToken: process.env.MBM_OFFICIAL_CONFORMANCE_ACCESS_TOKEN,
232
+ scenario: process.env.MBM_OFFICIAL_CONFORMANCE_SCENARIO,
233
+ specVersion: process.env.MBM_OFFICIAL_CONFORMANCE_SPEC_VERSION || "2026-07-28",
234
+ timeoutMs: process.env.MBM_OFFICIAL_CONFORMANCE_TIMEOUT_MS,
235
+ verbose: process.env.MBM_OFFICIAL_CONFORMANCE_VERBOSE === "1",
236
+ expectedFailures: process.env.MBM_OFFICIAL_CONFORMANCE_BASELINE,
237
+ });
238
+ process.stdout.write(result.stdout);
239
+ process.stderr.write(result.stderr);
240
+ process.exitCode = result.code;
241
+ }
242
+
243
+ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) await main();
@@ -0,0 +1,36 @@
1
+ export function persistentActivationSpawnOptions({ cwd, env = process.env } = {}) {
2
+ if (typeof cwd !== "string" || !cwd) {
3
+ throw new TypeError("persistent activation subprocess requires cwd");
4
+ }
5
+ if (!env || typeof env !== "object" || Array.isArray(env)) {
6
+ throw new TypeError("persistent activation subprocess requires an environment record");
7
+ }
8
+ // The activation child owns bounded deployment, network, relay, and service
9
+ // stages plus transactional cleanup. An outer timeout could SIGKILL the child
10
+ // while detached helpers remain alive and before compensation releases locks.
11
+ return { cwd, env, encoding: "utf8", windowsHide: true };
12
+ }
13
+
14
+ export function persistentCandidateFailureMessage(output, { cli, stateRoot, previousRuntime = null } = {}) {
15
+ const detail = String(output || "").trim() || "activation subprocess exited unsuccessfully";
16
+ if (!/foreground daemon is active/i.test(detail)) return `persistent candidate activation failed: ${detail}`;
17
+ const quotedCli = JSON.stringify(String(cli || "machine-mcp"));
18
+ const quotedStateRoot = JSON.stringify(String(stateRoot || ""));
19
+ const recovery = previousRuntime?.cli && previousRuntime?.pid
20
+ ? [
21
+ `Verified foreground runtime: ${previousRuntime.version || "unknown"} (pid ${previousRuntime.pid}).`,
22
+ "Stop that foreground daemon, then restore its existing login service with:",
23
+ `node ${JSON.stringify(previousRuntime.cli)} service start`,
24
+ `node ${quotedCli} service status --workspace ${JSON.stringify(previousRuntime.workspace)} --state-dir ${quotedStateRoot}`,
25
+ "Retry candidate activation only after status reports provider active and a verified service daemon for that workspace.",
26
+ ]
27
+ : [
28
+ "The foreground runtime could not be independently resolved to a trusted installed CLI.",
29
+ "Keep it running and inspect its daemon lock and command line before attempting a manual service recovery.",
30
+ ];
31
+ return [
32
+ `persistent candidate activation failed: ${detail}`,
33
+ "No Worker deployment or service replacement was started.",
34
+ ...recovery,
35
+ ].join("\n");
36
+ }
@@ -38,3 +38,15 @@ export function validateCandidateManifest(value, expected = {}) {
38
38
  prepared_at: new Date(preparedAt).toISOString(),
39
39
  });
40
40
  }
41
+
42
+ export function assertCandidateMatchesCurrentSource(manifest, current) {
43
+ const packageName = String(current?.packageName || "");
44
+ const packageVersion = String(current?.packageVersion || "");
45
+ const promotionDigest = String(current?.promotionDigest || "");
46
+ if (manifest.package_name !== packageName || manifest.package_version !== packageVersion) {
47
+ throw new Error("release candidate is stale: package identity no longer matches the current source");
48
+ }
49
+ if (!/^[0-9a-f]{64}$/.test(promotionDigest) || manifest.promotion_content_sha256 !== promotionDigest) {
50
+ throw new Error("release candidate is stale: promotion content digest no longer matches the current source");
51
+ }
52
+ }
@@ -0,0 +1,65 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { realpathSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { withOwnerStateLock } from "../src/local/owner-state-lock.mjs";
5
+
6
+ const CONFIRMATION_FLAG = "--owner-terminal-confirm";
7
+ const LOCK_DIRECTORY = "machine-bridge-release-state";
8
+ const LOCK_FILE = "github-publication.lock";
9
+ const GIT_METADATA_TIMEOUT_MS = 30_000;
10
+
11
+ export function assertOwnerTerminalPublication(options = {}) {
12
+ let argv = process.argv.slice(2);
13
+ if (Array.isArray(options.argv)) argv = options.argv.map(String);
14
+ const stdin = options.stdin === undefined ? process.stdin : options.stdin;
15
+ const stdout = options.stdout === undefined ? process.stdout : options.stdout;
16
+ const stderr = options.stderr === undefined ? process.stderr : options.stderr;
17
+ if (!argv.includes(CONFIRMATION_FLAG)) {
18
+ throw new Error(`GitHub publication requires an explicit owner terminal invocation with ${CONFIRMATION_FLAG}`);
19
+ }
20
+ if (stdin.isTTY !== true || stdout.isTTY !== true || stderr.isTTY !== true) {
21
+ throw new Error("GitHub publication requires an interactive owner terminal; background jobs, MCP calls, CI, and redirected sessions are not accepted");
22
+ }
23
+ return Object.freeze({ confirmation_flag: CONFIRMATION_FLAG, interactive_terminal: true });
24
+ }
25
+
26
+ export function withGithubPublicationLock(root, callback, options = {}) {
27
+ const stateRoot = options.stateRoot
28
+ ? resolve(String(options.stateRoot))
29
+ : resolveGithubPublicationStateRoot(root);
30
+ return withOwnerStateLock(stateRoot, callback, {
31
+ purpose: "github-publication",
32
+ fileName: LOCK_FILE,
33
+ label: "GitHub publication",
34
+ timeoutMs: options.timeoutMs ?? 1_000,
35
+ pollMs: options.pollMs ?? 25,
36
+ maxAgeMs: options.maxAgeMs ?? 6 * 60 * 60 * 1_000,
37
+ });
38
+ }
39
+
40
+ export function resolveGithubPublicationStateRoot(root) {
41
+ const repositoryRoot = resolve(String(root || ""));
42
+ const result = spawnSync("git", ["rev-parse", "--git-common-dir"], {
43
+ cwd: repositoryRoot,
44
+ encoding: "utf8",
45
+ timeout: GIT_METADATA_TIMEOUT_MS,
46
+ killSignal: "SIGKILL",
47
+ windowsHide: true,
48
+ });
49
+ return githubPublicationStateRootFromGitResult(repositoryRoot, result);
50
+ }
51
+
52
+ export function githubPublicationStateRootFromGitResult(repositoryRoot, result) {
53
+ if (result?.error || result?.status !== 0) throw new Error("could not resolve the common Git publication state directory");
54
+ const path = String(result?.stdout ?? "").trim();
55
+ if (!path || path.includes("\0")) throw new Error("Git returned an invalid common publication state directory");
56
+ const commonDirectory = resolve(repositoryRoot, path);
57
+ try {
58
+ return join(realpathSync.native(commonDirectory), LOCK_DIRECTORY);
59
+ } catch {
60
+ throw new Error("common Git publication state directory is unavailable");
61
+ }
62
+ }
63
+
64
+ export const githubPublicationConfirmationFlag = CONFIRMATION_FLAG;
65
+ export const githubPublicationGitTimeoutMs = GIT_METADATA_TIMEOUT_MS;
@@ -1,7 +1,7 @@
1
1
  export function tagSyncError({ scope = "local", tag, head, commit }) {
2
2
  const label = scope === "remote" ? "remote tag" : "local tag";
3
3
  if (!commit) {
4
- return `${label} ${tag} is missing; run npm run release before npm publish`;
4
+ return `${label} ${tag} is missing; run the applicable owner-terminal GitHub release command with --owner-terminal-confirm before npm publish`;
5
5
  }
6
6
  if (commit !== head) {
7
7
  return `${label} ${tag} points to ${commit}, not HEAD ${head}`;