machine-bridge-mcp 2.0.0 → 3.0.0-beta.9

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 (143) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/CONTRIBUTING.md +20 -25
  3. package/README.md +21 -14
  4. package/SECURITY.md +147 -123
  5. package/browser-extension/manifest.json +3 -2
  6. package/browser-extension/service-worker.js +31 -12
  7. package/docs/ARCHITECTURE.md +27 -13
  8. package/docs/AUDIT.md +85 -2
  9. package/docs/CLIENTS.md +2 -2
  10. package/docs/ENGINEERING.md +9 -3
  11. package/docs/GETTING_STARTED.md +10 -9
  12. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  13. package/docs/LOGGING.md +12 -3
  14. package/docs/MANAGED_JOBS.md +24 -29
  15. package/docs/MULTI_ACCOUNT.md +98 -37
  16. package/docs/OPERATIONS.md +33 -22
  17. package/docs/OVERVIEW.md +7 -7
  18. package/docs/PROJECT_STANDARDS.md +9 -5
  19. package/docs/RELEASING.md +171 -64
  20. package/docs/TESTING.md +19 -7
  21. package/docs/THREAT_MODEL.md +138 -67
  22. package/docs/TOOL_REFERENCE.md +2 -2
  23. package/docs/UPGRADING.md +102 -18
  24. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  25. package/package.json +36 -5
  26. package/scripts/candidate-runtime-store.mjs +38 -0
  27. package/scripts/check-plan.mjs +19 -0
  28. package/scripts/check-runner.mjs +4 -1
  29. package/scripts/commit-message-check.mjs +5 -2
  30. package/scripts/coverage-check.mjs +30 -2
  31. package/scripts/github-push.mjs +11 -0
  32. package/scripts/github-release.mjs +38 -7
  33. package/scripts/install-published-prerelease.mjs +167 -0
  34. package/scripts/local-release-acceptance.mjs +23 -12
  35. package/scripts/npm-publication-policy.mjs +16 -0
  36. package/scripts/prerelease-activation.mjs +79 -0
  37. package/scripts/privacy-check.mjs +7 -5
  38. package/scripts/promotion-digest.mjs +121 -0
  39. package/scripts/publish-npm.mjs +51 -0
  40. package/scripts/published-release.mjs +80 -0
  41. package/scripts/release-acceptance.mjs +30 -1
  42. package/scripts/release-candidate-manifest.mjs +40 -0
  43. package/scripts/release-channel.mjs +106 -0
  44. package/scripts/release-impact-check.mjs +12 -22
  45. package/scripts/release-soak.mjs +237 -0
  46. package/scripts/start-release-candidate.mjs +113 -7
  47. package/src/local/account-access.mjs +6 -0
  48. package/src/local/account-admin.mjs +34 -16
  49. package/src/local/agent-context.mjs +45 -42
  50. package/src/local/app-automation.mjs +9 -4
  51. package/src/local/authority-context.mjs +106 -0
  52. package/src/local/browser-bridge-http.mjs +4 -1
  53. package/src/local/browser-bridge.mjs +14 -9
  54. package/src/local/browser-broker-routes.mjs +4 -0
  55. package/src/local/browser-broker-server.mjs +5 -4
  56. package/src/local/browser-extension-identity.mjs +50 -0
  57. package/src/local/browser-extension-protocol.mjs +9 -4
  58. package/src/local/browser-operation-service.mjs +7 -0
  59. package/src/local/browser-pairing-http.mjs +36 -0
  60. package/src/local/browser-pairing-store.mjs +56 -47
  61. package/src/local/cli-account-admin.mjs +41 -7
  62. package/src/local/cli-activate.mjs +90 -0
  63. package/src/local/cli-approval.mjs +14 -58
  64. package/src/local/cli-local-admin.mjs +5 -15
  65. package/src/local/cli-options.mjs +11 -7
  66. package/src/local/cli-service.mjs +35 -5
  67. package/src/local/cli.mjs +112 -58
  68. package/src/local/daemon-process.mjs +6 -0
  69. package/src/local/delegated-process-sandbox.mjs +147 -0
  70. package/src/local/device-identity.mjs +157 -48
  71. package/src/local/device-root-provider.mjs +87 -0
  72. package/src/local/errors.mjs +25 -3
  73. package/src/local/git-service.mjs +16 -15
  74. package/src/local/job-runner.mjs +35 -27
  75. package/src/local/log.mjs +14 -1
  76. package/src/local/macos-trust-broker.mjs +320 -0
  77. package/src/local/managed-job-lock.mjs +18 -0
  78. package/src/local/managed-job-projection.mjs +9 -1
  79. package/src/local/managed-job-runner.mjs +13 -1
  80. package/src/local/managed-job-storage.mjs +2 -1
  81. package/src/local/managed-job-terminal.mjs +138 -0
  82. package/src/local/managed-jobs.mjs +88 -32
  83. package/src/local/operation-authorization.mjs +75 -239
  84. package/src/local/operation-risk.mjs +28 -0
  85. package/src/local/operation-state-lock.mjs +7 -86
  86. package/src/local/owner-state-lock.mjs +104 -0
  87. package/src/local/policy.mjs +19 -0
  88. package/src/local/process-execution.mjs +21 -9
  89. package/src/local/process-sessions.mjs +27 -17
  90. package/src/local/process-tracker.mjs +30 -6
  91. package/src/local/process-tree-ownership.mjs +59 -0
  92. package/src/local/process-tree.mjs +22 -18
  93. package/src/local/relay-connection.mjs +47 -35
  94. package/src/local/relay-diagnostics.mjs +65 -0
  95. package/src/local/remote-configuration.mjs +48 -0
  96. package/src/local/runtime-activation.mjs +141 -0
  97. package/src/local/runtime-diagnostics.mjs +21 -0
  98. package/src/local/runtime-relay.mjs +9 -8
  99. package/src/local/runtime-reporting.mjs +8 -3
  100. package/src/local/runtime-tool-handlers.mjs +8 -8
  101. package/src/local/runtime.mjs +73 -39
  102. package/src/local/secure-file.mjs +5 -1
  103. package/src/local/security-audit-log.mjs +204 -0
  104. package/src/local/service-convergence.mjs +17 -1
  105. package/src/local/service-ownership.mjs +17 -0
  106. package/src/local/service-restart-handoff.mjs +50 -0
  107. package/src/local/service-restart-scheduler.mjs +49 -0
  108. package/src/local/service-status.mjs +47 -0
  109. package/src/local/service.mjs +142 -43
  110. package/src/local/state-inventory.mjs +21 -3
  111. package/src/local/state.mjs +155 -17
  112. package/src/local/stdio.mjs +5 -0
  113. package/src/local/tool-executor.mjs +39 -6
  114. package/src/local/tool-result-boundary.mjs +51 -0
  115. package/src/local/tools.mjs +1 -0
  116. package/src/local/trusted-executable.mjs +53 -0
  117. package/src/local/trusted-git-executable.mjs +34 -0
  118. package/src/local/trusted-github-cli.mjs +24 -0
  119. package/src/local/windows-service.mjs +20 -0
  120. package/src/local/worker-deployment.mjs +3 -4
  121. package/src/local/worker-secret-file.mjs +2 -2
  122. package/src/local/workspace-file-service.mjs +34 -31
  123. package/src/shared/admin-auth.d.mts +2 -1
  124. package/src/shared/admin-auth.mjs +3 -3
  125. package/src/shared/device-session-auth.d.mts +20 -0
  126. package/src/shared/device-session-auth.mjs +51 -0
  127. package/src/shared/server-metadata.json +1 -1
  128. package/src/shared/tool-catalog.json +2 -2
  129. package/src/worker/account-admin.ts +23 -22
  130. package/src/worker/authority.ts +9 -2
  131. package/src/worker/daemon-auth.ts +45 -67
  132. package/src/worker/daemon-sockets.ts +12 -2
  133. package/src/worker/device-session-verifier.ts +129 -0
  134. package/src/worker/dpop.ts +151 -0
  135. package/src/worker/http.ts +47 -16
  136. package/src/worker/index.ts +53 -16
  137. package/src/worker/nonce-store.ts +1 -5
  138. package/src/worker/oauth-client-admin.ts +45 -0
  139. package/src/worker/oauth-controller.ts +55 -6
  140. package/src/worker/oauth-refresh-families.ts +21 -7
  141. package/src/worker/oauth-state.ts +6 -0
  142. package/src/worker/oauth-tokens.ts +28 -8
  143. package/src/worker/websocket-protocol.ts +8 -2
@@ -0,0 +1,65 @@
1
+ export const APPLICATION_PROXY_ROUTE_SCOPE = "application-proxy-selection-only";
2
+
3
+ export function relayStatusSnapshot(state, now = Date.now()) {
4
+ const current = Number(now) || 0;
5
+ return {
6
+ authenticated: state.authenticated === true,
7
+ ready: state.ready === true,
8
+ readiness_probe_delivered: state.readinessProbeDelivered === true,
9
+ closed: state.closed === true,
10
+ network_route: state.networkRoute,
11
+ network_route_scope: state.networkRouteScope,
12
+ reconnect_attempt: state.reconnectAttempt,
13
+ outage_active: state.outageStartedAt > 0,
14
+ outage_count: state.outageCount,
15
+ outage_started_at: isoTimestamp(state.outageStartedAt),
16
+ outage_duration_ms: state.outageStartedAt > 0 ? Math.max(0, current - state.outageStartedAt) : 0,
17
+ last_close_category: state.outageCount > 0 ? state.lastCloseCategory : null,
18
+ last_close_code: state.outageCount > 0 ? state.lastCloseCode : null,
19
+ last_transport_error_class: state.outageCount > 0 ? (state.lastTransportErrorClass || null) : null,
20
+ last_disconnected_at: isoTimestamp(state.lastDisconnectedAt),
21
+ last_ready_at: isoTimestamp(state.lastReadyAt),
22
+ last_ready_duration_ms: state.lastReadyDurationMs,
23
+ last_reconnect_delay_ms: state.lastReconnectDelayMs,
24
+ next_reconnect_at: isoTimestamp(state.nextReconnectAt),
25
+ next_reconnect_in_ms: state.nextReconnectAt > 0 ? Math.max(0, state.nextReconnectAt - current) : 0,
26
+ session_generation: state.sessionGeneration,
27
+ };
28
+ }
29
+
30
+ export function relayOutageFields(state, now, cause) {
31
+ const current = Number(now) || 0;
32
+ return {
33
+ event: "relay.outage.active",
34
+ outage_seconds: roundSeconds(Math.max(0, current - state.outageStartedAt)),
35
+ attempts: state.outageAttempts,
36
+ cause,
37
+ close_category: state.lastCloseCategory,
38
+ close_code: state.lastCloseCode,
39
+ network_route: state.networkRoute,
40
+ network_route_scope: state.networkRouteScope,
41
+ next_reconnect_in_ms: state.nextReconnectAt > 0 ? Math.max(0, state.nextReconnectAt - current) : 0,
42
+ ...(state.lastTransportErrorClass ? { error_class: state.lastTransportErrorClass } : {}),
43
+ };
44
+ }
45
+
46
+ export function relayRecoveryFields(state, outageMs) {
47
+ return {
48
+ event: "relay.outage.recovered",
49
+ outage_seconds: roundSeconds(outageMs),
50
+ attempts: state.outageAttempts,
51
+ close_category: state.lastCloseCategory,
52
+ close_code: state.lastCloseCode,
53
+ network_route: state.networkRoute,
54
+ network_route_scope: state.networkRouteScope,
55
+ ...(state.lastTransportErrorClass ? { error_class: state.lastTransportErrorClass } : {}),
56
+ };
57
+ }
58
+
59
+ function isoTimestamp(value) {
60
+ return Number(value) > 0 ? new Date(Number(value)).toISOString() : null;
61
+ }
62
+
63
+ function roundSeconds(milliseconds) {
64
+ return Math.max(1, Math.round(Number(milliseconds || 0) / 1000));
65
+ }
@@ -0,0 +1,48 @@
1
+ import { effectiveLogFormat, effectiveLogLevel } from "./cli-options.mjs";
2
+ import { resolvePolicy } from "./cli-policy.mjs";
3
+ import { ensurePreferredDeviceRoot } from "./device-root-provider.mjs";
4
+ import { createLogger } from "./log.mjs";
5
+ import {
6
+ ensureWorkerSecrets,
7
+ promotePendingDeviceIdentity,
8
+ saveState,
9
+ } from "./state.mjs";
10
+ import { ensureWorkerDeployment } from "./worker-deployment.mjs";
11
+
12
+ export async function convergeRemoteConfiguration({ args, state }) {
13
+ const workerName = validateWorkerName(args.workerName);
14
+ ensureWorkerSecrets(state, {
15
+ rotateSecrets: Boolean(args.rotateSecrets),
16
+ deferDeviceRotation: true,
17
+ workerName,
18
+ allowWorkerRename: Boolean(args.forceWorker),
19
+ });
20
+ if (!state.worker.pendingDeviceIdentity) {
21
+ const candidate = await ensurePreferredDeviceRoot({
22
+ profileDir: state.paths.profileDir,
23
+ workspaceHash: state.workspace.hash,
24
+ existing: args.rotateSecrets ? null : state.worker.deviceIdentity,
25
+ rotate: Boolean(args.rotateSecrets),
26
+ });
27
+ if (candidate.keyId !== state.worker.deviceIdentity.keyId) state.worker.pendingDeviceIdentity = candidate;
28
+ }
29
+ state.policy = resolvePolicy(args, state.policy);
30
+ state.policy.updatedAt = new Date().toISOString();
31
+ saveState(state);
32
+ const logger = createLogger({
33
+ level: args.json ? "error" : effectiveLogLevel(args),
34
+ format: effectiveLogFormat(args),
35
+ component: "worker",
36
+ });
37
+ await ensureWorkerDeployment(state, args, { logger });
38
+ if (promotePendingDeviceIdentity(state)) saveState(state);
39
+ }
40
+
41
+ export function validateWorkerName(value) {
42
+ if (value === undefined || value === null || value === false) return undefined;
43
+ const name = String(value).trim();
44
+ if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(name)) {
45
+ throw new Error("--worker-name must be 1-63 lowercase letters, digits, or hyphens, and cannot start or end with a hyphen");
46
+ }
47
+ return name;
48
+ }
@@ -0,0 +1,141 @@
1
+ export async function activatePersistentRuntime(options = {}) {
2
+ const required = [
3
+ "acquireStartupLock",
4
+ "stopAutostart",
5
+ "acquireDaemonLock",
6
+ "prepareRemoteState",
7
+ "createRuntime",
8
+ "installAutostart",
9
+ "startAutostart",
10
+ "inspectDaemon",
11
+ "checkWorker",
12
+ ];
13
+ for (const name of required) {
14
+ if (typeof options[name] !== "function") throw new TypeError(`runtime activation requires ${name}`);
15
+ }
16
+
17
+ const startupLock = await options.acquireStartupLock();
18
+ let startupReleased = false;
19
+ let daemonLock = null;
20
+ let candidateRuntime = null;
21
+ let candidateRelayVerified = false;
22
+ try {
23
+ const providerStop = await options.stopAutostart();
24
+ if (providerStop?.ok === false && providerStop?.active === true) {
25
+ throw new Error("the existing autostart service could not be stopped before activation");
26
+ }
27
+
28
+ daemonLock = await options.acquireDaemonLock();
29
+ if (!daemonLock?.acquired) {
30
+ const owner = daemonLock?.owner || {};
31
+ if (owner.mode === "foreground") {
32
+ throw new Error(`a foreground daemon is active (pid ${owner.pid || "unknown"}); stop it explicitly before activation`);
33
+ }
34
+ throw new Error(`the existing daemon could not be taken over (pid ${owner.pid || "unknown"})`);
35
+ }
36
+
37
+ const readiness = await options.prepareRemoteState();
38
+ candidateRuntime = options.createRuntime({ daemonLock, readiness });
39
+ if (!candidateRuntime || typeof candidateRuntime.start !== "function" || typeof candidateRuntime.stop !== "function") {
40
+ throw new TypeError("runtime activation candidate must expose start and stop");
41
+ }
42
+ await candidateRuntime.start();
43
+ candidateRelayVerified = true;
44
+
45
+ const installed = await options.installAutostart();
46
+ const terminalError = candidateRuntime.terminalError?.();
47
+ if (terminalError) throw terminalError;
48
+ if (installed?.ok === false) {
49
+ throw new Error(`autostart installation failed (${installed.provider || "unknown"})`);
50
+ }
51
+
52
+ candidateRuntime.stop();
53
+ candidateRuntime = null;
54
+ daemonLock.release();
55
+ daemonLock = null;
56
+ startupLock.release();
57
+ startupReleased = true;
58
+
59
+ const serviceStart = await options.startAutostart();
60
+ if (serviceStart?.ok === false) {
61
+ throw new Error(`autostart start failed (${serviceStart.provider || "unknown"})`);
62
+ }
63
+
64
+ const convergence = await waitForActivatedRuntime({
65
+ expectedVersion: options.expectedVersion,
66
+ inspectDaemon: options.inspectDaemon,
67
+ checkWorker: options.checkWorker,
68
+ maximumAttempts: options.maximumAttempts,
69
+ wait: options.wait,
70
+ });
71
+ if (!convergence.ok) {
72
+ throw new Error(`persistent runtime activation did not converge (${convergence.reason})`);
73
+ }
74
+ return {
75
+ ok: true,
76
+ serviceStart,
77
+ convergence,
78
+ candidateRelayVerified,
79
+ };
80
+ } catch (error) {
81
+ const cleanupErrors = [];
82
+ try { candidateRuntime?.stop?.(); } catch (failure) { cleanupErrors.push(failure); }
83
+ try { daemonLock?.release?.(); } catch (failure) { cleanupErrors.push(failure); }
84
+ if (!startupReleased) {
85
+ try { startupLock.release(); }
86
+ catch (failure) { cleanupErrors.push(failure); }
87
+ }
88
+ if (cleanupErrors.length) {
89
+ throw new AggregateError(
90
+ [error, ...cleanupErrors],
91
+ "persistent runtime activation failed and local cleanup was incomplete; inspect the daemon and service state before retrying",
92
+ );
93
+ }
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ export async function waitForActivatedRuntime({
99
+ inspectDaemon,
100
+ checkWorker,
101
+ expectedVersion,
102
+ maximumAttempts = 240,
103
+ wait = defaultWait,
104
+ } = {}) {
105
+ if (typeof inspectDaemon !== "function" || typeof checkWorker !== "function") {
106
+ throw new TypeError("runtime activation requires daemon and Worker inspectors");
107
+ }
108
+ const attempts = boundedAttempts(maximumAttempts);
109
+ let daemon = null;
110
+ let worker = null;
111
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
112
+ daemon = await inspectDaemon();
113
+ if (daemon?.alive && daemon?.verified_service_daemon && daemon?.version === expectedVersion) {
114
+ worker = await checkWorker();
115
+ if (worker?.ok && worker?.version === expectedVersion) {
116
+ return { ok: true, attempts: attempt, daemon, worker };
117
+ }
118
+ }
119
+ if (attempt < attempts) await wait(attempt);
120
+ }
121
+ const reasons = [];
122
+ if (!daemon?.alive) reasons.push("daemon_not_running");
123
+ else if (!daemon?.verified_service_daemon) reasons.push(`daemon_identity_${daemon?.identity_reason || "unverified"}`);
124
+ else if (daemon?.version !== expectedVersion) reasons.push(`daemon_version_${daemon?.version || "unknown"}`);
125
+ if (!worker?.ok) reasons.push(`worker_${worker?.error || "not_ready"}`);
126
+ else if (worker?.version !== expectedVersion) reasons.push(`worker_version_${worker?.version || "unknown"}`);
127
+ return { ok: false, attempts, daemon, worker, reason: reasons.join(",") || "activation_not_converged" };
128
+ }
129
+
130
+ function boundedAttempts(value) {
131
+ const parsed = Number(value);
132
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 1200) {
133
+ throw new Error("runtime activation attempts must be between 1 and 1200");
134
+ }
135
+ return parsed;
136
+ }
137
+
138
+ function defaultWait(attempt) {
139
+ const delay = Math.min(1_000, 100 + attempt * 25);
140
+ return new Promise((resolvePromise) => { setTimeout(resolvePromise, delay); });
141
+ }
@@ -11,6 +11,7 @@ export async function diagnoseRuntime({
11
11
  runProcess,
12
12
  probeShell,
13
13
  managedJobManager,
14
+ relayStatus = () => null,
14
15
  throwIfCancelled,
15
16
  }, context = {}) {
16
17
  throwIfCancelled(context);
@@ -24,6 +25,25 @@ export async function diagnoseRuntime({
24
25
  detail: `profile=${policy.profile}; exec_mode=${policy.execMode}; unrestricted_paths=${policy.unrestrictedPaths}`,
25
26
  }];
26
27
 
28
+ const relay = typeof relayStatus === "function" ? relayStatus() : null;
29
+ checks.push(relay ? {
30
+ layer: "remote-relay",
31
+ ok: relay.ready === true,
32
+ network_route: relay.network_route || "unknown",
33
+ network_route_scope: relay.network_route_scope || "unknown",
34
+ outage_active: relay.outage_active === true,
35
+ outage_count: Number(relay.outage_count) || 0,
36
+ last_close_category: relay.last_close_category || null,
37
+ last_close_code: Number.isFinite(Number(relay.last_close_code)) ? Number(relay.last_close_code) : null,
38
+ last_transport_error_class: relay.last_transport_error_class || null,
39
+ last_disconnected_at: relay.last_disconnected_at || null,
40
+ last_ready_at: relay.last_ready_at || null,
41
+ last_ready_duration_ms: Number(relay.last_ready_duration_ms) || 0,
42
+ next_reconnect_in_ms: Number(relay.next_reconnect_in_ms) || 0,
43
+ } : {
44
+ layer: "remote-relay", ok: false, skipped: true, transport: "stdio-or-local",
45
+ });
46
+
27
47
  const probe = join(runtimeDir, `.diagnostic-${process.pid}-${randomBytes(6).toString("hex")}`);
28
48
  try {
29
49
  await writeFile(probe, "ok\n", { mode: 0o600, flag: "wx" });
@@ -82,6 +102,7 @@ export async function diagnoseRuntime({
82
102
  interpretation: {
83
103
  tool_call_blocked_before_response: "host/platform or connector gateway",
84
104
  diagnostic_reached_daemon_but_spawn_failed: "local OS, endpoint security, shell configuration, or Machine Bridge policy",
105
+ system_network_stack_scope: "application proxy selection only; an operating-system VPN or TUN may still intercept the relay connection",
85
106
  managed_job_accepted_then_later_tools_blocked: "job continues independently; inspect with local CLI or a later read_job call",
86
107
  },
87
108
  policy,
@@ -1,15 +1,15 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { RelayConnection } from "./relay-connection.mjs";
3
- import { createDaemonAuthentication, createDaemonPreflightHeaders } from "./device-identity.mjs";
3
+ import { createDaemonAuthentication, createDaemonPreflightHeaders, createDeviceSessionIdentity, validateDeviceSessionIdentity } from "./device-identity.mjs";
4
4
  import { MCP_SUPPORTED_PROTOCOL_VERSIONS, SERVER_NAME } from "./tools.mjs";
5
5
  import { normalizeAccountRole } from "./account-access.mjs";
6
6
  import { clampInteger } from "./numbers.mjs";
7
7
  import { isPlainRecord } from "./records.mjs";
8
-
9
8
  export const MAX_RELAY_MESSAGE_BYTES = 8 * 1024 * 1024;
10
-
11
9
  export function createRuntimeRelayConnection(runtime, { workerUrl, deviceIdentity, expectedVersion, onFatal }) {
12
- if (!workerUrl) return null;
10
+ if (!workerUrl || !deviceIdentity) return null;
11
+ const sessionIdentity = deviceIdentity?.certificate ? validateDeviceSessionIdentity(deviceIdentity)
12
+ : createDeviceSessionIdentity(deviceIdentity, workerUrl, SERVER_NAME, String(expectedVersion || ""));
13
13
  return new RelayConnection({
14
14
  workerUrl,
15
15
  logger: runtime.logger,
@@ -17,7 +17,7 @@ export function createRuntimeRelayConnection(runtime, { workerUrl, deviceIdentit
17
17
  expectedServer: SERVER_NAME,
18
18
  expectedVersion: String(expectedVersion || ""),
19
19
  connectionHeaders: () => createDaemonPreflightHeaders(
20
- deviceIdentity,
20
+ sessionIdentity,
21
21
  workerUrl,
22
22
  SERVER_NAME,
23
23
  String(expectedVersion || ""),
@@ -28,7 +28,7 @@ export function createRuntimeRelayConnection(runtime, { workerUrl, deviceIdentit
28
28
  tools: runtime.tools(),
29
29
  policy: runtime.policy,
30
30
  protocol_versions: MCP_SUPPORTED_PROTOCOL_VERSIONS,
31
- authentication: await createDaemonAuthentication(deviceIdentity, welcome, runtime.relayInstanceId),
31
+ authentication: await createDaemonAuthentication(sessionIdentity, welcome, runtime.relayInstanceId),
32
32
  }),
33
33
  onMessage: (data, relayContext) => handleRelayData(runtime, data, relayContext),
34
34
  onDisconnect: () => runtime.handleRelayDisconnect(),
@@ -90,8 +90,9 @@ function normalizeRelayAuthorization(value) {
90
90
  const accountId = typeof value.account_id === "string" && /^acct_[A-Za-z0-9_-]{20,96}$/.test(value.account_id) ? value.account_id : "";
91
91
  const accountVersion = Number(value.account_version);
92
92
  const clientId = typeof value.client_id === "string" && /^mcp_client_[A-Za-z0-9_-]{43}$/.test(value.client_id) ? value.client_id : "";
93
+ const familyId = typeof value.family_id === "string" && /^mcp_family_[A-Za-z0-9_-]{43}$/.test(value.family_id) ? value.family_id : "";
93
94
  let role;
94
95
  try { role = normalizeAccountRole(value.role); } catch { return null; }
95
- if (!accountId || !clientId || !Number.isInteger(accountVersion) || accountVersion < 1) return null;
96
- return Object.freeze({ account_id: accountId, account_version: accountVersion, client_id: clientId, role });
96
+ if (!accountId || !clientId || !familyId || !Number.isInteger(accountVersion) || accountVersion < 1) return null;
97
+ return Object.freeze({ account_id: accountId, account_version: accountVersion, client_id: clientId, family_id: familyId, role });
97
98
  }
@@ -77,16 +77,19 @@ export async function buildProjectOverview({
77
77
  displayPath,
78
78
  policy,
79
79
  toolNames,
80
+ daemonPolicy = policy,
81
+ daemonToolNames = toolNames,
80
82
  capabilityObserver,
81
83
  listTopLevel,
82
- runProcess,
84
+ runInternalProcess,
85
+ gitExecutable,
83
86
  safeErrorMessage,
84
87
  throwIfCancelled,
85
88
  }, context = {}) {
86
89
  throwIfCancelled(context);
87
90
  const top = await listTopLevel(context).catch((error) => ({ error: safeErrorMessage(error), entries: [] }));
88
- const git = await runProcess(
89
- "git",
91
+ const git = await runInternalProcess(
92
+ gitExecutable(),
90
93
  ["-c", "core.fsmonitor=false", "-C", workspace, "rev-parse", "--show-toplevel"],
91
94
  10_000,
92
95
  true,
@@ -99,6 +102,8 @@ export async function buildProjectOverview({
99
102
  gitRoot: git.code === 0 ? displayPath(git.stdout.trim()) : "",
100
103
  policy,
101
104
  tools: ["server_info", ...toolNames],
105
+ daemonPolicy,
106
+ daemonTools: ["server_info", ...daemonToolNames],
102
107
  capabilityRouting: capabilityObserver.snapshot(),
103
108
  topLevel: top.entries || [],
104
109
  };
@@ -1,7 +1,7 @@
1
1
  import { clampInteger } from "./numbers.mjs";
2
2
 
3
3
  const RUNTIME_TOOL_HANDLERS = Object.freeze({
4
- server_info: (runtime) => runtime.runtimeInfo(),
4
+ server_info: (runtime, _args, context) => runtime.runtimeInfo(context),
5
5
  project_overview: (runtime, _args, context) => runtime.projectOverview(context),
6
6
  session_bootstrap: (runtime, args, context) => runtime.sessionBootstrap(args, context),
7
7
  agent_context: (runtime, args, context) => runtime.agentContextManager.agentContext(args, context),
@@ -25,7 +25,7 @@ const RUNTIME_TOOL_HANDLERS = Object.freeze({
25
25
  browser_fill_form: (runtime, args, context) => runtime.browserBridgeManager.fillForm(args, context),
26
26
  browser_screenshot: (runtime, args, context) => runtime.browserBridgeManager.screenshot(args, context),
27
27
  browser_upload_files: (runtime, args, context) => runtime.browserBridgeManager.uploadFiles(args, context),
28
- list_roots: (runtime) => runtime.listRoots(),
28
+ list_roots: (runtime, _args, context) => runtime.listRoots(context),
29
29
  list_dir: (runtime, args, context) => runtime.listDir(args.path || ".", context),
30
30
  list_files: (runtime, args, context) => runtime.listFiles(args.path || ".", clampInteger(args.max_files, 1000, 1, 10000), context),
31
31
  read_file: (runtime, args, context) => runtime.readFile(args, context),
@@ -39,13 +39,13 @@ const RUNTIME_TOOL_HANDLERS = Object.freeze({
39
39
  git_log: (runtime, args, context) => runtime.gitLog(args, context),
40
40
  git_show: (runtime, args, context) => runtime.gitShow(args, context),
41
41
  diagnose_runtime: (runtime, _args, context) => runtime.diagnoseRuntime(context),
42
- list_local_resources: (runtime) => runtime.managedJobManager.listResources(),
42
+ list_local_resources: (runtime, _args, context) => runtime.managedJobManager.listResources(context),
43
43
  generate_ssh_key_resource: (runtime, args, context) => runtime.generateSshKeyResource(args, context),
44
- stage_job: (runtime, args) => runtime.managedJobManager.stage(args),
45
- start_job: (runtime, args) => runtime.managedJobManager.start(args),
46
- list_jobs: (runtime, args) => runtime.managedJobManager.list(args),
47
- read_job: (runtime, args) => runtime.managedJobManager.read(args),
48
- cancel_job: (runtime, args) => runtime.managedJobManager.cancel(args),
44
+ stage_job: (runtime, args, context) => runtime.managedJobManager.stage(args, context),
45
+ start_job: (runtime, args, context) => runtime.managedJobManager.start(args, context),
46
+ list_jobs: (runtime, args, context) => runtime.managedJobManager.list(args, context),
47
+ read_job: (runtime, args, context) => runtime.managedJobManager.read(args, context),
48
+ cancel_job: (runtime, args, context) => runtime.managedJobManager.cancel(args, context),
49
49
  run_process: (runtime, args, context) => runtime.runDirectProcess(args, context),
50
50
  start_process: (runtime, args, context) => runtime.processSessionManager.start(args, context),
51
51
  read_process: (runtime, args, context) => runtime.processSessionManager.read(args, context),