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
@@ -1,6 +1,8 @@
1
1
  export async function activatePersistentRuntime(options = {}) {
2
2
  const required = [
3
3
  "acquireStartupLock",
4
+ "acquireServiceLock",
5
+ "inspectActivationOwnership",
4
6
  "stopAutostart",
5
7
  "acquireDaemonLock",
6
8
  "prepareRemoteState",
@@ -13,20 +15,43 @@ export async function activatePersistentRuntime(options = {}) {
13
15
  for (const name of required) {
14
16
  if (typeof options[name] !== "function") throw new TypeError(`runtime activation requires ${name}`);
15
17
  }
18
+ const expectedVersion = requiredExpectedVersion(options.expectedVersion);
19
+ const maximumAttempts = boundedAttempts(options.maximumAttempts ?? 240);
20
+ const candidateStartAttempts = boundedCandidateStartAttempts(options.candidateStartAttempts ?? 3);
21
+ const candidateRetryWait = optionalFunction(options.candidateRetryWait, "candidateRetryWait", defaultCandidateRetryWait);
22
+ const convergenceWait = optionalFunction(options.wait, "wait", defaultWait);
23
+ const restorePreviousAutostart = optionalFunction(options.restorePreviousAutostart, "restorePreviousAutostart", options.startAutostart);
24
+ if (options.repairRemoteState !== undefined && typeof options.repairRemoteState !== "function") {
25
+ throw new TypeError("runtime activation repairRemoteState must be a function");
26
+ }
16
27
 
17
- const startupLock = await options.acquireStartupLock();
28
+ let startupLock = null;
18
29
  let startupReleased = false;
30
+ let serviceLock = null;
19
31
  let daemonLock = null;
20
32
  let candidateRuntime = null;
21
33
  let candidateRelayVerified = false;
34
+ let remotePrepared = false;
35
+ let autostartInstalled = false;
22
36
  let providerStopped = false;
37
+ let previousServiceRuntime = null;
23
38
  let serviceStarted = false;
24
39
  try {
25
- const providerStop = await options.stopAutostart();
26
- if (providerStop?.ok === false && providerStop?.active === true) {
27
- throw new Error("the existing autostart service could not be stopped before activation");
40
+ const acquiredServiceLock = await options.acquireServiceLock();
41
+ if (!acquiredServiceLock?.acquired) {
42
+ const owner = acquiredServiceLock?.owner || {};
43
+ throw new Error(`another machine-service operation is active (pid ${owner.pid || "unknown"})`);
28
44
  }
29
- providerStopped = providerStop?.active_before === true && providerStop?.active === false;
45
+ requireReleasableLock(acquiredServiceLock, "machine-service");
46
+ serviceLock = acquiredServiceLock;
47
+ const acquiredStartupLock = await options.acquireStartupLock();
48
+ requireReleasableLock(acquiredStartupLock, "startup");
49
+ startupLock = acquiredStartupLock;
50
+ const ownership = validateActivationOwnership(await options.inspectActivationOwnership());
51
+ providerStopped = ownership.previousServiceRuntimeActive;
52
+ previousServiceRuntime = ownership.previousServiceRuntime;
53
+ const providerStop = await options.stopAutostart();
54
+ providerStopped = validateProviderStop(providerStop) || providerStopped;
30
55
 
31
56
  daemonLock = await options.acquireDaemonLock();
32
57
  if (!daemonLock?.acquired) {
@@ -36,21 +61,40 @@ export async function activatePersistentRuntime(options = {}) {
36
61
  }
37
62
  throw new Error(`the existing daemon could not be taken over (pid ${owner.pid || "unknown"})`);
38
63
  }
64
+ requireReleasableLock(daemonLock, "daemon");
39
65
 
40
- const readiness = await options.prepareRemoteState();
41
- candidateRuntime = options.createRuntime({ daemonLock, readiness });
42
- if (!candidateRuntime || typeof candidateRuntime.start !== "function" || typeof candidateRuntime.stop !== "function") {
43
- throw new TypeError("runtime activation candidate must expose start and stop");
66
+ let readiness;
67
+ try {
68
+ readiness = await options.prepareRemoteState({
69
+ onRemotePrepared() { remotePrepared = true; },
70
+ });
71
+ remotePrepared = true;
72
+ } catch (error) {
73
+ if (error?.deploymentSucceeded === true) remotePrepared = true;
74
+ throw error;
44
75
  }
45
- await candidateRuntime.start();
76
+
77
+ const candidate = await startCandidateWithRecovery({
78
+ attempts: candidateStartAttempts,
79
+ daemonLock,
80
+ readiness,
81
+ createRuntime: options.createRuntime,
82
+ repairRemoteState: options.repairRemoteState,
83
+ wait: candidateRetryWait,
84
+ onRuntimeCreated(runtime) { candidateRuntime = runtime; },
85
+ onRuntimeStopped() { candidateRuntime = null; },
86
+ onRemoteRepaired() { remotePrepared = true; },
87
+ });
88
+ candidateRuntime = candidate.runtime;
46
89
  candidateRelayVerified = true;
47
90
 
48
91
  const installed = await options.installAutostart();
49
92
  const terminalError = candidateRuntime.terminalError?.();
50
93
  if (terminalError) throw terminalError;
51
- if (installed?.ok === false) {
52
- throw new Error(`autostart installation failed (${installed.provider || "unknown"})`);
94
+ if (installed?.ok !== true) {
95
+ throw new Error(`autostart installation failed (${installed?.provider || "unknown"})`);
53
96
  }
97
+ autostartInstalled = true;
54
98
 
55
99
  candidateRuntime.stop();
56
100
  candidateRuntime = null;
@@ -60,55 +104,295 @@ export async function activatePersistentRuntime(options = {}) {
60
104
  startupReleased = true;
61
105
 
62
106
  const serviceStart = await options.startAutostart();
63
- if (serviceStart?.ok === false) {
64
- throw new Error(`autostart start failed (${serviceStart.provider || "unknown"})`);
65
- }
107
+ requireActiveServiceStart(serviceStart, "autostart start");
66
108
  serviceStarted = true;
67
109
 
68
110
  const convergence = await waitForActivatedRuntime({
69
- expectedVersion: options.expectedVersion,
111
+ expectedVersion,
70
112
  inspectDaemon: options.inspectDaemon,
71
113
  checkWorker: options.checkWorker,
72
- maximumAttempts: options.maximumAttempts,
73
- wait: options.wait,
114
+ maximumAttempts,
115
+ wait: convergenceWait,
74
116
  });
75
117
  if (!convergence.ok) {
76
118
  throw new Error(`persistent runtime activation did not converge (${convergence.reason})`);
77
119
  }
120
+ serviceLock.release();
121
+ serviceLock = null;
78
122
  return {
79
123
  ok: true,
80
124
  serviceStart,
81
125
  convergence,
82
126
  candidateRelayVerified,
127
+ candidateRecoveryRedeployed: candidate.repaired,
83
128
  };
84
129
  } catch (error) {
85
- const cleanupErrors = [];
86
- try { candidateRuntime?.stop?.(); } catch (failure) { cleanupErrors.push(failure); }
87
- try { daemonLock?.release?.(); } catch (failure) { cleanupErrors.push(failure); }
88
- if (!startupReleased) {
89
- try { startupLock.release(); }
90
- catch (failure) { cleanupErrors.push(failure); }
91
- }
130
+ throw await failedActivationError({
131
+ error,
132
+ candidateRuntime,
133
+ daemonLock,
134
+ startupLock,
135
+ startupReleased,
136
+ serviceLock,
137
+ providerStopped,
138
+ serviceStarted,
139
+ remotePrepared,
140
+ autostartInstalled,
141
+ installAutostart: options.installAutostart,
142
+ startAutostart: options.startAutostart,
143
+ restorePreviousAutostart,
144
+ inspectPreviousAutostart: options.inspectPreviousAutostart,
145
+ previousServiceRuntime,
146
+ restoreWait: convergenceWait,
147
+ restoreMaximumAttempts: options.maximumAttempts,
148
+ });
149
+ }
150
+ }
151
+
152
+ async function failedActivationError({
153
+ error,
154
+ candidateRuntime,
155
+ daemonLock,
156
+ startupLock,
157
+ startupReleased,
158
+ serviceLock,
159
+ providerStopped,
160
+ serviceStarted,
161
+ remotePrepared,
162
+ autostartInstalled,
163
+ installAutostart,
164
+ startAutostart,
165
+ restorePreviousAutostart,
166
+ inspectPreviousAutostart,
167
+ previousServiceRuntime,
168
+ restoreWait,
169
+ restoreMaximumAttempts,
170
+ }) {
171
+ const cleanupErrors = [];
172
+ try { candidateRuntime?.stop?.(); } catch (failure) { cleanupErrors.push(failure); }
173
+ try { daemonLock?.release?.(); } catch (failure) { cleanupErrors.push(failure); }
174
+ if (!startupReleased && startupLock) {
175
+ try { startupLock.release(); }
176
+ catch (failure) { cleanupErrors.push(failure); }
177
+ }
178
+ let recovery = null;
179
+ const cleanupIncomplete = error?.cleanupIncomplete === true || cleanupErrors.length > 0;
180
+ if (!cleanupIncomplete) {
92
181
  try {
93
- await restoreStoppedProvider(options.startAutostart, { providerStopped, serviceStarted });
182
+ recovery = await restoreCompatibleProvider({
183
+ installAutostart,
184
+ startAutostart,
185
+ restorePreviousAutostart,
186
+ inspectPreviousAutostart,
187
+ previousServiceRuntime,
188
+ restoreWait,
189
+ restoreMaximumAttempts,
190
+ providerStopped,
191
+ serviceStarted,
192
+ candidateServiceRequired: remotePrepared,
193
+ candidateDefinitionRequired: remotePrepared && !autostartInstalled,
194
+ });
94
195
  } catch (failure) { cleanupErrors.push(failure); }
95
- if (cleanupErrors.length) {
96
- throw new AggregateError(
97
- [error, ...cleanupErrors],
98
- "persistent runtime activation failed and local cleanup was incomplete; inspect the daemon and service state before retrying",
99
- );
196
+ }
197
+ if (serviceLock) {
198
+ try { serviceLock.release(); } catch (failure) { cleanupErrors.push(failure); }
199
+ }
200
+ if (cleanupErrors.length) {
201
+ return new AggregateError(
202
+ [error, ...cleanupErrors],
203
+ "persistent runtime activation failed and local cleanup was incomplete; inspect the daemon and service state before retrying",
204
+ );
205
+ }
206
+ if (recovery?.candidateServiceStarted) return activationFailureWithRecovery(error);
207
+ return error;
208
+ }
209
+
210
+ async function startCandidateWithRecovery({
211
+ attempts = 3,
212
+ daemonLock,
213
+ readiness,
214
+ createRuntime,
215
+ repairRemoteState,
216
+ wait = defaultCandidateRetryWait,
217
+ onRuntimeCreated,
218
+ onRuntimeStopped,
219
+ onRemoteRepaired,
220
+ } = {}) {
221
+ const maximumAttempts = boundedCandidateStartAttempts(attempts);
222
+ let currentReadiness = readiness;
223
+ let repaired = false;
224
+ let lastError = null;
225
+ for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
226
+ const runtime = createRuntime({ daemonLock, readiness: currentReadiness });
227
+ if (!runtime || typeof runtime.start !== "function" || typeof runtime.stop !== "function") {
228
+ throw new TypeError("runtime activation candidate must expose start and stop");
229
+ }
230
+ onRuntimeCreated(runtime);
231
+ try {
232
+ await runtime.start();
233
+ return { runtime, repaired };
234
+ } catch (error) {
235
+ lastError = error;
236
+ let stopFailure = null;
237
+ try { runtime.stop(); } catch (failure) { stopFailure = failure; }
238
+ finally { onRuntimeStopped(); }
239
+ if (stopFailure) {
240
+ const cleanupError = new AggregateError(
241
+ [error, stopFailure],
242
+ "candidate relay start failed and runtime cleanup was incomplete",
243
+ );
244
+ cleanupError.cleanupIncomplete = true;
245
+ throw cleanupError;
246
+ }
247
+ if (!recoverableCandidateStartError(error) || attempt >= maximumAttempts) throw error;
248
+ if (!repaired && typeof repairRemoteState === "function") {
249
+ let repairPrepared = false;
250
+ const markRemoteRepaired = () => {
251
+ if (repairPrepared) return;
252
+ repairPrepared = true;
253
+ onRemoteRepaired();
254
+ };
255
+ currentReadiness = await repairRemoteState({
256
+ attempt,
257
+ error,
258
+ readiness: currentReadiness,
259
+ onRemotePrepared: markRemoteRepaired,
260
+ });
261
+ repaired = true;
262
+ markRemoteRepaired();
263
+ }
264
+ await wait(attempt);
100
265
  }
101
- throw error;
102
266
  }
267
+ throw lastError || new Error("candidate relay did not start");
103
268
  }
104
269
 
270
+ function recoverableCandidateStartError(error) {
271
+ return error?.code === "relay_authentication_failed";
272
+ }
105
273
 
106
- async function restoreStoppedProvider(startAutostart, { providerStopped, serviceStarted }) {
107
- if (!providerStopped || serviceStarted) return;
108
- const restored = await startAutostart();
109
- if (restored?.ok === false) {
110
- throw new Error(`previous autostart service could not be restored (${restored.provider || "unknown"})`);
274
+ async function restoreCompatibleProvider({
275
+ installAutostart,
276
+ startAutostart,
277
+ restorePreviousAutostart,
278
+ inspectPreviousAutostart,
279
+ previousServiceRuntime,
280
+ restoreWait,
281
+ restoreMaximumAttempts,
282
+ providerStopped,
283
+ serviceStarted,
284
+ candidateServiceRequired,
285
+ candidateDefinitionRequired,
286
+ }) {
287
+ if (serviceStarted || (!candidateServiceRequired && !providerStopped)) {
288
+ return { candidateServiceStarted: false };
289
+ }
290
+ if (candidateDefinitionRequired) {
291
+ const installed = await installAutostart();
292
+ if (installed?.ok !== true) {
293
+ throw new Error(`compatible candidate autostart could not be installed (${installed?.provider || "unknown"})`);
294
+ }
111
295
  }
296
+ const restored = candidateServiceRequired
297
+ ? await startAutostart()
298
+ : await restorePreviousAutostart();
299
+ const qualifier = candidateServiceRequired ? "compatible candidate" : "previous";
300
+ requireActiveServiceStart(restored, `${qualifier} autostart service recovery`);
301
+ if (!candidateServiceRequired) {
302
+ if (!previousServiceRuntime || typeof inspectPreviousAutostart !== "function") {
303
+ throw new Error("previous autostart service recovery lacks verified runtime identity evidence");
304
+ }
305
+ const convergence = await waitForRestoredServiceRuntime({
306
+ inspectDaemon: () => inspectPreviousAutostart(previousServiceRuntime),
307
+ expectedVersion: previousServiceRuntime.version,
308
+ maximumAttempts: restoreMaximumAttempts,
309
+ wait: restoreWait,
310
+ });
311
+ if (!convergence.ok) {
312
+ throw new Error(`previous autostart service recovery did not converge (${convergence.reason})`);
313
+ }
314
+ }
315
+ return { candidateServiceStarted: candidateServiceRequired };
316
+ }
317
+
318
+ function validateActivationOwnership(value) {
319
+ const daemon = value?.daemon || {};
320
+ const provider = value?.provider || {};
321
+ if (typeof provider.active !== "boolean") {
322
+ throw new Error(`the machine autostart state could not be verified before activation (${provider.provider || "unknown"})`);
323
+ }
324
+ if (daemon.alive === true) {
325
+ if (daemon.mode === "foreground" || daemon.identity_reason === "foreground_daemon") {
326
+ throw new Error(`a foreground daemon is active (pid ${daemon.pid || "unknown"}); stop it explicitly before activation`);
327
+ }
328
+ if (daemon.verified_service_daemon !== true || daemon.mode !== "service") {
329
+ throw new Error(`the existing workspace daemon identity could not be verified before activation (${daemon.identity_reason || "unknown"})`);
330
+ }
331
+ }
332
+ const previousServiceRuntimeActive = daemon.alive === true
333
+ && daemon.verified_service_daemon === true
334
+ && daemon.mode === "service";
335
+ if (provider.active === true && !previousServiceRuntimeActive) {
336
+ throw new Error("refusing to stop the active machine service because the selected workspace does not own its verified daemon");
337
+ }
338
+ let previousServiceRuntime = null;
339
+ if (previousServiceRuntimeActive) {
340
+ const evidence = value?.previousRuntime || {};
341
+ if (typeof evidence.version !== "string" || !evidence.version
342
+ || typeof evidence.entryScript !== "string" || !evidence.entryScript) {
343
+ throw new Error("the previous service daemon identity evidence is incomplete before activation");
344
+ }
345
+ previousServiceRuntime = { version: evidence.version, entryScript: evidence.entryScript };
346
+ }
347
+ return { previousServiceRuntimeActive, previousServiceRuntime };
348
+ }
349
+
350
+ function validateProviderStop(result) {
351
+ if (result?.ok !== true) {
352
+ throw new Error(`the existing autostart service could not be stopped before activation (${result?.provider || "unknown"})`);
353
+ }
354
+ if (typeof result.active_before !== "boolean" || result.active !== false || typeof result.restore_required !== "boolean") {
355
+ throw new Error(`the existing autostart stop state could not be verified before activation (${result?.provider || "unknown"})`);
356
+ }
357
+ return result.restore_required;
358
+ }
359
+
360
+ function requireActiveServiceStart(result, operation) {
361
+ if (result?.ok !== true || result.active !== true) {
362
+ throw new Error(`${operation} did not reach an active persistent service (${result?.provider || "unknown"})`);
363
+ }
364
+ }
365
+
366
+ function activationFailureWithRecovery(error) {
367
+ const message = error instanceof Error && error.message ? error.message : String(error || "activation failed");
368
+ const recoveredMessage = `${message}; a compatible candidate service was installed and started for automatic recovery`;
369
+ const recovered = error instanceof AggregateError
370
+ ? new AggregateError(error.errors, recoveredMessage, { cause: error })
371
+ : new Error(recoveredMessage, { cause: error });
372
+ if (error?.code) recovered.code = error.code;
373
+ recovered.activationRecovery = "candidate_service_started";
374
+ return recovered;
375
+ }
376
+
377
+ export async function waitForRestoredServiceRuntime({
378
+ inspectDaemon, expectedVersion, maximumAttempts = 240, wait = defaultWait,
379
+ } = {}) {
380
+ if (typeof inspectDaemon !== "function") throw new TypeError("previous runtime recovery requires a daemon inspector");
381
+ const attempts = boundedAttempts(maximumAttempts);
382
+ let daemon = null;
383
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
384
+ daemon = await inspectDaemon();
385
+ if (daemon?.alive && daemon?.verified_service_daemon
386
+ && daemon?.mode === "service" && daemon?.version === expectedVersion) {
387
+ return { ok: true, attempts: attempt, daemon };
388
+ }
389
+ if (attempt < attempts) await wait(attempt);
390
+ }
391
+ const reason = !daemon?.alive ? "daemon_not_running"
392
+ : daemon?.verified_service_daemon !== true ? `daemon_identity_${daemon?.identity_reason || "unverified"}`
393
+ : daemon?.mode !== "service" ? `daemon_mode_${daemon?.mode || "unknown"}`
394
+ : `daemon_version_${daemon?.version || "unknown"}`;
395
+ return { ok: false, attempts, daemon, reason };
112
396
  }
113
397
 
114
398
  export async function waitForActivatedRuntime({
@@ -126,7 +410,9 @@ export async function waitForActivatedRuntime({
126
410
  let worker = null;
127
411
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
128
412
  daemon = await inspectDaemon();
129
- if (daemon?.alive && daemon?.verified_service_daemon && daemon?.version === expectedVersion) {
413
+ if (daemon?.alive && daemon?.verified_service_daemon
414
+ && daemon?.startup_readiness_verified === true
415
+ && daemon?.version === expectedVersion) {
130
416
  worker = await checkWorker();
131
417
  if (worker?.ok && worker?.version === expectedVersion) {
132
418
  return { ok: true, attempts: attempt, daemon, worker };
@@ -137,12 +423,32 @@ export async function waitForActivatedRuntime({
137
423
  const reasons = [];
138
424
  if (!daemon?.alive) reasons.push("daemon_not_running");
139
425
  else if (!daemon?.verified_service_daemon) reasons.push(`daemon_identity_${daemon?.identity_reason || "unverified"}`);
426
+ else if (daemon?.startup_readiness_verified !== true) reasons.push("daemon_startup_readiness_unverified");
140
427
  else if (daemon?.version !== expectedVersion) reasons.push(`daemon_version_${daemon?.version || "unknown"}`);
141
428
  if (!worker?.ok) reasons.push(`worker_${worker?.error || "not_ready"}`);
142
429
  else if (worker?.version !== expectedVersion) reasons.push(`worker_version_${worker?.version || "unknown"}`);
143
430
  return { ok: false, attempts, daemon, worker, reason: reasons.join(",") || "activation_not_converged" };
144
431
  }
145
432
 
433
+ function requiredExpectedVersion(value) {
434
+ if (typeof value !== "string" || !/^[0-9A-Za-z.+-]{1,128}$/.test(value)) {
435
+ throw new TypeError("runtime activation expectedVersion must be a bounded package version string");
436
+ }
437
+ return value;
438
+ }
439
+
440
+ function optionalFunction(value, name, fallback) {
441
+ if (value === undefined) return fallback;
442
+ if (typeof value !== "function") throw new TypeError(`runtime activation ${name} must be a function`);
443
+ return value;
444
+ }
445
+
446
+ function requireReleasableLock(lock, name) {
447
+ if (!lock || typeof lock.release !== "function") {
448
+ throw new TypeError(`runtime activation ${name} lock must expose release`);
449
+ }
450
+ }
451
+
146
452
  function boundedAttempts(value) {
147
453
  const parsed = Number(value);
148
454
  if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 1200) {
@@ -151,7 +457,20 @@ function boundedAttempts(value) {
151
457
  return parsed;
152
458
  }
153
459
 
460
+ function boundedCandidateStartAttempts(value) {
461
+ const parsed = Number(value);
462
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 10) {
463
+ throw new Error("candidate relay start attempts must be between 1 and 10");
464
+ }
465
+ return parsed;
466
+ }
467
+
154
468
  function defaultWait(attempt) {
155
469
  const delay = Math.min(1_000, 100 + attempt * 25);
156
470
  return new Promise((resolvePromise) => { setTimeout(resolvePromise, delay); });
157
471
  }
472
+
473
+ function defaultCandidateRetryWait(attempt) {
474
+ const delay = Math.min(2_000, 250 * (2 ** Math.max(0, attempt - 1)));
475
+ return new Promise((resolvePromise) => { setTimeout(resolvePromise, delay); });
476
+ }
@@ -1,4 +1,6 @@
1
1
  import { classifyOperationalError } from "./log.mjs";
2
+ import { buildExecutionRouting } from "./execution-routing.mjs";
3
+ import { policyAllowsTool } from "./policy.mjs";
2
4
 
3
5
  export async function sessionBootstrap({
4
6
  agentContextManager,
@@ -7,9 +9,11 @@ export async function sessionBootstrap({
7
9
  policy,
8
10
  }, args = {}, context = {}) {
9
11
  const bootstrap = await agentContextManager.sessionBootstrap(args, context);
12
+ const applicationAllowed = policyAllowsTool(policy, "list_local_applications");
13
+ const browserAllowed = policyAllowsTool(policy, "browser_status");
10
14
  bootstrap.local_automation = {
11
- applications: appAutomationManager.capabilities(),
12
- browser: policy.profile === "full" ? {
15
+ applications: applicationAllowed ? appAutomationManager.capabilities() : null,
16
+ browser: browserAllowed ? {
13
17
  existing_profile: true,
14
18
  extension_bridge: true,
15
19
  status_tool: "browser_status",
@@ -27,7 +31,9 @@ export async function resolveTaskCapabilities({
27
31
  }, args = {}, context = {}) {
28
32
  const result = await agentContextManager.resolveTaskCapabilities(args, context);
29
33
  const task = String(args.task || "");
30
- if (policy.profile === "full") {
34
+ const applicationAllowed = policyAllowsTool(policy, "list_local_applications");
35
+ const browserAllowed = policyAllowsTool(policy, "browser_status");
36
+ if (applicationAllowed) {
31
37
  let applications;
32
38
  try {
33
39
  applications = await appAutomationManager.listApplications({ query: "", max_results: 500 }, context);
@@ -49,7 +55,7 @@ export async function resolveTaskCapabilities({
49
55
  .map(({ application, score }) => ({ ...application, score }));
50
56
  } else {
51
57
  result.application_matches = [];
52
- result.application_discovery = { available: false, warning_count: 0, truncated: false, reason: "policy" };
58
+ result.application_discovery = { available: false, warning_count: 0, truncated: false, reason: "effective_policy" };
53
59
  }
54
60
  if (result.application_matches.length) {
55
61
  result.recommended_tools = [...new Set([
@@ -60,10 +66,20 @@ export async function resolveTaskCapabilities({
60
66
  "operate_local_application",
61
67
  ])];
62
68
  }
63
- result.browser_backend = policy.profile === "full"
69
+ result.browser_backend = browserAllowed
64
70
  ? { tool: "browser_status", existing_profile: true, extension_bridge: true }
65
71
  : null;
66
- result.routing_observability = "Call server_info or project_overview to verify that bootstrap and task capability resolution reached the local runtime.";
72
+ const routing = buildExecutionRouting(task, {
73
+ policy,
74
+ seedTools: result.recommended_tools,
75
+ commandRelevant: (result.command_matches?.[0]?.score || 0) >= 3,
76
+ skillRelevant: (result.skill_matches?.[0]?.score || 0) >= 3,
77
+ applicationMatches: result.application_matches,
78
+ browserAvailable: browserAllowed,
79
+ });
80
+ result.execution_routing = routing;
81
+ result.recommended_tools = routing.recommended_tools;
82
+ result.routing_observability = "Call server_info or project_overview to verify that bootstrap and task capability resolution reached the local runtime. Routing is advisory and does not restrict direct shell or any other tool allowed by the effective policy.";
67
83
  capabilityObserver.recordResolution(task, result);
68
84
  return result;
69
85
  }
@@ -3,12 +3,16 @@ import { rm, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { classifyOperationalError } from "./log.mjs";
5
5
  import { readBoundedFile } from "./workspace-file-service.mjs";
6
+ import { systemNetworkRouteCheck } from "./system-network-route.mjs";
7
+
8
+ export const RUNTIME_DIAGNOSTIC_PROCESS_TIMEOUT_MS = 30_000;
6
9
 
7
10
  export async function diagnoseRuntime({
8
11
  policy,
9
12
  runtimeDir,
10
13
  workspace,
11
14
  runProcess,
15
+ runFixedInternal,
12
16
  probeShell,
13
17
  managedJobManager,
14
18
  relayStatus = () => null,
@@ -44,6 +48,8 @@ export async function diagnoseRuntime({
44
48
  layer: "remote-relay", ok: false, skipped: true, transport: "stdio-or-local",
45
49
  });
46
50
 
51
+ checks.push(await systemNetworkRouteCheck({ runFixedInternal, classifyError: classifyOperationalError, context }));
52
+
47
53
  const probe = join(runtimeDir, `.diagnostic-${process.pid}-${randomBytes(6).toString("hex")}`);
48
54
  try {
49
55
  await writeFile(probe, "ok\n", { mode: 0o600, flag: "wx" });
@@ -59,7 +65,7 @@ export async function diagnoseRuntime({
59
65
  const direct = await runProcess(
60
66
  process.execPath,
61
67
  ["-e", "process.stdout.write('ok')"],
62
- 5000,
68
+ RUNTIME_DIAGNOSTIC_PROCESS_TIMEOUT_MS,
63
69
  true,
64
70
  1024,
65
71
  context,
@@ -75,7 +81,7 @@ export async function diagnoseRuntime({
75
81
  }
76
82
 
77
83
  if (policy.execMode === "shell") {
78
- const result = await probeShell(context)
84
+ const result = await probeShell(context, RUNTIME_DIAGNOSTIC_PROCESS_TIMEOUT_MS)
79
85
  .catch((error) => ({ code: 127, error_class: classifyOperationalError(error) }));
80
86
  checks.push({
81
87
  layer: "local-shell",
@@ -103,6 +109,7 @@ export async function diagnoseRuntime({
103
109
  tool_call_blocked_before_response: "host/platform or connector gateway",
104
110
  diagnostic_reached_daemon_but_spawn_failed: "local OS, endpoint security, shell configuration, or Machine Bridge policy",
105
111
  system_network_stack_scope: "application proxy selection only; an operating-system VPN or TUN may still intercept the relay connection",
112
+ tunnel_default_route_detected: "the operating-system route is carried by a VPN/TUN; node selection and repair remain outside Machine Bridge",
106
113
  managed_job_accepted_then_later_tools_blocked: "job continues independently; inspect with local CLI or a later read_job call",
107
114
  },
108
115
  policy,
@@ -334,7 +334,20 @@ export class LocalRuntime {
334
334
  this.relayResumeSessionId = 0;
335
335
  return true;
336
336
  }
337
- if (message.type === "pong") return true;
337
+ if (message.type === "pong") {
338
+ this.relayCallRecovery.pulse();
339
+ return true;
340
+ }
341
+ if (message.type === "tool_result_ack") {
342
+ if (!isRelayReadyContext(relayContext, this.relay)
343
+ || typeof message.id !== "string"
344
+ || !/^call_[A-Za-z0-9_-]{8,240}$/.test(message.id)) {
345
+ this.handleRelayProtocolViolation("invalid_tool_result_ack");
346
+ return true;
347
+ }
348
+ this.relayCallRecovery.acknowledge(message.id);
349
+ return true;
350
+ }
338
351
  if (message.type === "error") {
339
352
  this.relay?.handleServerError(message);
340
353
  return true;
@@ -516,7 +529,8 @@ export class LocalRuntime {
516
529
  runtimeDir: this.runtimeDir,
517
530
  workspace: this.workspace,
518
531
  runProcess: (...args) => this.runProcess(...args),
519
- probeShell: (callContext) => this.processExecutionService.probeShell(callContext),
532
+ runFixedInternal: (...args) => this.processExecutionService.runFixedInternal(...args),
533
+ probeShell: (callContext, timeoutMs) => this.processExecutionService.probeShell(callContext, timeoutMs),
520
534
  managedJobManager: this.managedJobManager,
521
535
  relayStatus: () => this.relay?.status?.() || null,
522
536
  throwIfCancelled: (callContext) => this.throwIfCancelled(callContext),
@@ -533,7 +547,7 @@ export class LocalRuntime {
533
547
  agentContextManager: this.agentContextManager,
534
548
  appAutomationManager: this.appAutomationManager,
535
549
  capabilityObserver: this.capabilityObserver,
536
- policy: this.policy,
550
+ policy: this.effectivePolicy(context),
537
551
  }, args, context);
538
552
  }
539
553
 
@@ -542,7 +556,7 @@ export class LocalRuntime {
542
556
  agentContextManager: this.agentContextManager,
543
557
  appAutomationManager: this.appAutomationManager,
544
558
  capabilityObserver: this.capabilityObserver,
545
- policy: this.policy,
559
+ policy: this.effectivePolicy(context),
546
560
  }, args, context);
547
561
  }
548
562