@tencent-ai/agent-harness 0.1.0 → 0.1.1

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 (41) hide show
  1. package/README.md +19 -7
  2. package/bin/native/darwin-arm64/agent-harness-process-snapshot.node +0 -0
  3. package/bin/native/darwin-x64/agent-harness-process-snapshot.node +0 -0
  4. package/cli/dist/codebuddy-headless.js +1 -1
  5. package/cli/dist/codebuddy-server.js +1 -1
  6. package/cli/product.cloudhosted.json +2 -2
  7. package/cli/product.internal.json +2 -2
  8. package/cli/product.ioa.json +2 -2
  9. package/cli/product.json +2 -2
  10. package/cli/product.selfhosted.json +2 -2
  11. package/docs/authentication-guide.md +4 -2
  12. package/executor/lib/capsule/entry.mjs +2 -2
  13. package/lib/integration/executor-supervisor.d.ts +4 -1
  14. package/lib/integration/executor-supervisor.js +7 -3
  15. package/lib/integration/stdio-guardian-child.js +11 -21
  16. package/lib/integration/stdio-guardian.d.ts +2 -1
  17. package/lib/integration/stdio-guardian.js +7 -5
  18. package/lib/runtime/agent-harness.d.ts +3 -1
  19. package/lib/runtime/agent-harness.js +12 -3
  20. package/lib/runtime/codebuddy-code.d.ts +5 -1
  21. package/lib/runtime/codebuddy-code.js +13 -3
  22. package/lib/runtime/codebuddy-prewarm-pool.d.ts +2 -0
  23. package/lib/runtime/codebuddy-prewarm-pool.js +9 -1
  24. package/lib/runtime/codebuddy-process.d.ts +2 -1
  25. package/lib/runtime/codebuddy-process.js +5 -22
  26. package/lib/runtime/codebuddy-resource-management.d.ts +2 -0
  27. package/lib/runtime/codebuddy-resource-management.js +13 -1
  28. package/lib/runtime/codebuddy-resource-process.d.ts +5 -2
  29. package/lib/runtime/codebuddy-resource-process.js +28 -8
  30. package/lib/runtime/codebuddy-v2-private-child.js +14 -1
  31. package/lib/runtime/node-runtime.d.ts +35 -0
  32. package/lib/runtime/node-runtime.js +46 -0
  33. package/lib/runtime/posix-contained-process.d.ts +3 -0
  34. package/lib/runtime/posix-contained-process.js +5 -3
  35. package/lib/runtime/posix-process-snapshot.d.ts +28 -0
  36. package/lib/runtime/posix-process-snapshot.js +183 -0
  37. package/lib/runtime/process-tree.d.ts +6 -8
  38. package/lib/runtime/process-tree.js +9 -37
  39. package/lib/transport/network.d.ts +7 -1
  40. package/lib/transport/network.js +15 -1
  41. package/package.json +5 -3
@@ -15,6 +15,7 @@ const trace_context_1 = require("#agent-harness-executor/trace-context");
15
15
  const process_runtime_context_js_1 = require("../effect-runtime/process-runtime-context.js");
16
16
  const errors_js_1 = require("../internal/errors.js");
17
17
  const bounded_json_js_1 = require("../protocol/bounded-json.js");
18
+ const node_runtime_js_1 = require("../runtime/node-runtime.js");
18
19
  const process_containment_js_1 = require("../runtime/process-containment.js");
19
20
  const process_tree_js_1 = require("../runtime/process-tree.js");
20
21
  const windows_job_js_1 = require("../runtime/windows-job.js");
@@ -103,7 +104,7 @@ const CHILD_ENV_ALLOWLIST = Object.freeze([
103
104
  'https_proxy',
104
105
  'no_proxy',
105
106
  ]);
106
- function createExecutorChildEnvironment(source = process.env) {
107
+ function createExecutorChildEnvironment(source = process.env, nodeRuntime = (0, node_runtime_js_1.resolveNodeRuntimeLaunch)()) {
107
108
  const environment = {
108
109
  AGENT_HARNESS_PRIVATE_EXECUTOR: '1',
109
110
  };
@@ -113,7 +114,7 @@ function createExecutorChildEnvironment(source = process.env) {
113
114
  environment[name] = value;
114
115
  }
115
116
  }
116
- return environment;
117
+ return (0, node_runtime_js_1.applyNodeRuntimeEnvironment)(environment, nodeRuntime);
117
118
  }
118
119
  function createExecutorWindowsJobSpawnSpec(entryPath, forkOptions, launcherOptions = {}) {
119
120
  const { execArgv = process.execArgv, execPath = process.execPath, silent: _silent, ...spawnOptions } = forkOptions;
@@ -163,19 +164,21 @@ function resolveOptions(options) {
163
164
  requested: options.allowStdio ?? true,
164
165
  windowsJobObjectAttached: options.windowsJobObjectAttached,
165
166
  });
167
+ const nodeRuntime = options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)();
166
168
  return {
167
169
  allowStdio: stdioContainment.allowStdio,
168
170
  childEntryPath: options.childEntryPath ?? defaultChildEntryPath(),
169
171
  closeTimeoutMs: positive(options.closeTimeoutMs, DEFAULT_CLOSE_TIMEOUT_MS, 'closeTimeoutMs'),
170
172
  containment: options.containment ?? new process_containment_js_1.CodeBuddyProcessContainment(),
171
173
  crashWindowMs: positive(options.crashWindowMs, DEFAULT_CRASH_WINDOW_MS, 'crashWindowMs'),
172
- environment: createExecutorChildEnvironment(options.environment),
174
+ environment: createExecutorChildEnvironment(options.environment, nodeRuntime),
173
175
  healthIntervalMs: nonNegative(options.healthIntervalMs, DEFAULT_HEALTH_INTERVAL_MS, 'healthIntervalMs'),
174
176
  healthTimeoutMs: positive(options.healthTimeoutMs, DEFAULT_HEALTH_TIMEOUT_MS, 'healthTimeoutMs'),
175
177
  instanceId: options.instanceId ?? (0, node_crypto_1.randomUUID)(),
176
178
  maxCrashes: positive(options.maxCrashes, DEFAULT_MAX_CRASHES, 'maxCrashes'),
177
179
  maxPendingRequestBytes: positive(options.maxPendingRequestBytes, DEFAULT_MAX_PENDING_REQUEST_BYTES, 'maxPendingRequestBytes'),
178
180
  maxPendingRequests: positive(options.maxPendingRequests, DEFAULT_MAX_PENDING_REQUESTS, 'maxPendingRequests'),
181
+ nodeRuntime,
179
182
  onElicitation: options.onElicitation,
180
183
  ...(options.onFailure === undefined
181
184
  ? {}
@@ -650,6 +653,7 @@ class ExecutorProcessSupervisor {
650
653
  detached: this.options.platform !== 'win32',
651
654
  env: this.options.environment,
652
655
  execArgv: [],
656
+ execPath: this.options.nodeRuntime.executable,
653
657
  serialization: 'json',
654
658
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
655
659
  });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const node_child_process_1 = require("node:child_process");
3
+ const posix_process_snapshot_js_1 = require("../runtime/posix-process-snapshot.js");
4
4
  const process_tree_js_1 = require("../runtime/process-tree.js");
5
5
  const stdio_guardian_capture_js_1 = require("./stdio-guardian-capture.js");
6
6
  const stdio_guardian_protocol_js_1 = require("./stdio-guardian-protocol.js");
@@ -61,28 +61,18 @@ function signalProcessGroup(processGroupId, signal) {
61
61
  function delay(ms) {
62
62
  return new Promise(resolve => setTimeout(resolve, ms));
63
63
  }
64
- function captureOwnedProcessIds(rootPid) {
65
- const spec = (0, process_tree_js_1.buildPosixProcessListSpec)(process.platform);
66
- return new Promise((resolve, reject) => {
67
- (0, node_child_process_1.execFile)(spec.command, [...spec.args], {
68
- encoding: 'utf8',
69
- maxBuffer: 1024 * 1024,
70
- timeout: PROCESS_TREE_CAPTURE_TIMEOUT_MS,
71
- windowsHide: true,
72
- }, (error, stdout) => {
73
- if (error) {
74
- reject(error);
75
- return;
76
- }
77
- resolve((0, process_tree_js_1.collectPosixOwnedProcessIds)(typeof stdout === 'string' ? stdout : '', rootPid, process.pid));
78
- });
64
+ function captureOwnedProcessIds(rootPid, ownershipId) {
65
+ return (0, posix_process_snapshot_js_1.capturePosixOwnedProcessIds)(rootPid, process.platform, {
66
+ excludedPid: process.pid,
67
+ ownershipId,
68
+ timeoutMs: PROCESS_TREE_CAPTURE_TIMEOUT_MS,
79
69
  });
80
70
  }
81
- async function captureOwnedProcessIdsReliably(rootPid) {
71
+ async function captureOwnedProcessIdsReliably(rootPid, ownershipId) {
82
72
  let lastError;
83
73
  for (let attempt = 1; attempt <= PROCESS_TREE_CAPTURE_ATTEMPTS; attempt += 1) {
84
74
  try {
85
- return await captureOwnedProcessIds(rootPid);
75
+ return await captureOwnedProcessIds(rootPid, ownershipId);
86
76
  }
87
77
  catch (error) {
88
78
  lastError = error;
@@ -138,11 +128,11 @@ async function cleanProcessGroup(options, anchorPid, knownProcessIds) {
138
128
  && ((0, process_tree_js_1.processExists)(anchorPid)
139
129
  || (0, process_tree_js_1.processGroupExists)(options.targetProcessGroupId))) {
140
130
  try {
141
- // `ps` can transiently exceed its deadline while the Harness is
131
+ // A snapshot can transiently exceed its deadline while the Harness is
142
132
  // concurrently reaping several process trees. Cleanup still fails
143
133
  // closed if every bounded attempt fails, but one busy snapshot no
144
134
  // longer trips the process-wide containment fuse.
145
- const processIds = await captureOwnedProcessIdsReliably(anchorPid);
135
+ const processIds = await captureOwnedProcessIdsReliably(anchorPid, options.targetProcessGroupId);
146
136
  // Terminal capture must union with periodic observations. A
147
137
  // forkpty/setsid descendant can be reparented between the last
148
138
  // periodic snapshot and this final owner-rooted snapshot; replacing
@@ -249,7 +239,7 @@ async function runGuardianChild() {
249
239
  return true;
250
240
  }
251
241
  try {
252
- const processIds = await captureOwnedProcessIdsReliably(rootPid);
242
+ const processIds = await captureOwnedProcessIdsReliably(rootPid, options.targetProcessGroupId);
253
243
  // Replace rather than accumulate: long-lived guardians must not
254
244
  // retain an exited PID after the OS has reused it for an unrelated
255
245
  // process.
@@ -1,4 +1,5 @@
1
1
  import { type ChildProcess, type SpawnOptions } from 'node:child_process';
2
+ import { type NodeRuntimeLaunch } from '../runtime/node-runtime.js';
2
3
  import { CodeBuddyProcessContainment } from '../runtime/process-containment.js';
3
4
  import { type StdioMcpGuardianSnapshotMode } from './stdio-guardian-protocol.js';
4
5
  export type StdioMcpContainmentDecision = {
@@ -23,7 +24,7 @@ export interface ResolveStdioMcpContainmentOptions {
23
24
  * stdio stays disabled unless a real Job Object owner has already attached.
24
25
  */
25
26
  export declare function resolveStdioMcpContainment(options: ResolveStdioMcpContainmentOptions): StdioMcpContainmentDecision;
26
- export declare function createStdioMcpGuardianEnvironment(): NodeJS.ProcessEnv;
27
+ export declare function createStdioMcpGuardianEnvironment(nodeRuntime?: NodeRuntimeLaunch): NodeJS.ProcessEnv;
27
28
  export interface PosixStdioMcpGuardianOptions {
28
29
  readonly childEntryPath?: string;
29
30
  readonly containment?: CodeBuddyProcessContainment;
@@ -10,6 +10,7 @@ const node_child_process_1 = require("node:child_process");
10
10
  const node_fs_1 = require("node:fs");
11
11
  const node_path_1 = __importDefault(require("node:path"));
12
12
  const errors_js_1 = require("../internal/errors.js");
13
+ const node_runtime_js_1 = require("../runtime/node-runtime.js");
13
14
  const process_containment_js_1 = require("../runtime/process-containment.js");
14
15
  const process_tree_js_1 = require("../runtime/process-tree.js");
15
16
  const windows_job_js_1 = require("../runtime/windows-job.js");
@@ -54,10 +55,10 @@ function resolveStdioMcpContainment(options) {
54
55
  reason: 'windows_job_object_required',
55
56
  };
56
57
  }
57
- function createStdioMcpGuardianEnvironment() {
58
- return {
58
+ function createStdioMcpGuardianEnvironment(nodeRuntime = (0, node_runtime_js_1.resolveNodeRuntimeLaunch)()) {
59
+ return (0, node_runtime_js_1.applyNodeRuntimeEnvironment)({
59
60
  AGENT_HARNESS_PRIVATE_STDIO_GUARDIAN: '1',
60
- };
61
+ }, nodeRuntime);
61
62
  }
62
63
  function positiveInteger(value, fallback, name) {
63
64
  const resolved = value ?? fallback;
@@ -495,12 +496,13 @@ async function attachPosixStdioMcpGuardian(input) {
495
496
  String(options.pollIntervalMs),
496
497
  options.containmentMode,
497
498
  ];
499
+ const nodeRuntime = (0, node_runtime_js_1.resolveNodeRuntimeLaunch)();
498
500
  let child;
499
501
  try {
500
- child = options.spawnGuardian(process.execPath, args, {
502
+ child = options.spawnGuardian(nodeRuntime.executable, args, {
501
503
  cwd: node_path_1.default.dirname(options.childEntryPath),
502
504
  detached: true,
503
- env: createStdioMcpGuardianEnvironment(),
505
+ env: createStdioMcpGuardianEnvironment(nodeRuntime),
504
506
  stdio: ['pipe', 'pipe', 'pipe'],
505
507
  windowsHide: true,
506
508
  });
@@ -1,4 +1,5 @@
1
1
  import { type ChildProcess, spawn, type StdioOptions } from 'node:child_process';
2
+ import { type NodeRuntimeLaunch } from './node-runtime.js';
2
3
  export interface ResolveAgentHarnessBinPathOptions {
3
4
  exists?: (candidate: string) => boolean;
4
5
  readFile?: (candidate: string) => string;
@@ -11,6 +12,7 @@ export interface CreateAgentHarnessSpawnSpecOptions {
11
12
  cwd?: string;
12
13
  env?: NodeJS.ProcessEnv;
13
14
  nodeExecutable?: string;
15
+ nodeRuntime?: NodeRuntimeLaunch;
14
16
  processEnv?: NodeJS.ProcessEnv;
15
17
  }
16
18
  export interface AgentHarnessSpawnSpec {
@@ -24,5 +26,5 @@ export interface SpawnAgentHarnessOptions extends CreateAgentHarnessSpawnSpecOpt
24
26
  stdio?: StdioOptions;
25
27
  }
26
28
  export declare function resolveAgentHarnessBinPath({ exists, readFile, resolvePackageJson, }?: ResolveAgentHarnessBinPathOptions): string;
27
- export declare function createAgentHarnessSpawnSpec({ binPath, cliArgs, cwd, env, nodeExecutable, processEnv, }?: CreateAgentHarnessSpawnSpecOptions): AgentHarnessSpawnSpec;
29
+ export declare function createAgentHarnessSpawnSpec({ binPath, cliArgs, cwd, env, nodeExecutable, nodeRuntime, processEnv, }?: CreateAgentHarnessSpawnSpecOptions): AgentHarnessSpawnSpec;
28
30
  export declare function spawnAgentHarness({ spawnImpl, stdio, ...options }?: SpawnAgentHarnessOptions): ChildProcess;
@@ -40,6 +40,7 @@ const node_child_process_1 = require("node:child_process");
40
40
  const fs = __importStar(require("node:fs"));
41
41
  const path = __importStar(require("node:path"));
42
42
  const codebuddy_code_js_1 = require("./codebuddy-code.js");
43
+ const node_runtime_js_1 = require("./node-runtime.js");
43
44
  function resolveAgentHarnessBinPath({ exists = fs.existsSync, readFile = candidate => fs.readFileSync(candidate, 'utf8'),
44
45
  // Resolve relative to this module instead of either the workspace or
45
46
  // published package name. pnpm publishConfig.name intentionally makes
@@ -64,15 +65,23 @@ resolvePackageJson = () => path.resolve(__dirname, '../../package.json'), } = {}
64
65
  }
65
66
  return binPath;
66
67
  }
67
- function createAgentHarnessSpawnSpec({ binPath = resolveAgentHarnessBinPath(), cliArgs = [], cwd, env, nodeExecutable = process.execPath, processEnv, } = {}) {
68
+ function createAgentHarnessSpawnSpec({ binPath = resolveAgentHarnessBinPath(), cliArgs = [], cwd, env, nodeExecutable, nodeRuntime, processEnv, } = {}) {
68
69
  if (cliArgs.some(argument => argument.includes('\0'))) {
69
70
  throw new Error('Agent Harness CLI arguments must not contain NUL bytes');
70
71
  }
72
+ if (nodeExecutable !== undefined
73
+ && nodeRuntime !== undefined
74
+ && nodeExecutable !== nodeRuntime.executable) {
75
+ throw new Error('Agent Harness Node runtime executable is ambiguous');
76
+ }
77
+ const runtime = nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
78
+ ...(nodeExecutable === undefined ? {} : { executable: nodeExecutable }),
79
+ });
71
80
  return {
72
- command: nodeExecutable,
81
+ command: runtime.executable,
73
82
  args: [binPath, ...cliArgs],
74
83
  cwd,
75
- env: (0, codebuddy_code_js_1.buildCodeBuddyCodeEnv)(env, processEnv),
84
+ env: (0, codebuddy_code_js_1.buildCodeBuddyCodeEnv)(env, processEnv, { nodeRuntime: runtime }),
76
85
  };
77
86
  }
78
87
  function spawnAgentHarness({ spawnImpl = node_child_process_1.spawn, stdio = ['pipe', 'pipe', 'inherit'], ...options } = {}) {
@@ -1,4 +1,5 @@
1
1
  import { type ChildProcess, spawn, type StdioOptions } from 'node:child_process';
2
+ import { type NodeRuntimeLaunch } from './node-runtime.js';
2
3
  import type { ProductLaunchSnapshot } from './product-manager.js';
3
4
  export declare const CODEBUDDY_CODE_PACKAGE_NAME = "@tencent-ai/codebuddy-code";
4
5
  /** Package manifest for the CodeBuddy runtime embedded in this Harness build. */
@@ -21,6 +22,8 @@ export interface CreateCodeBuddyCodeSpawnSpecOptions {
21
22
  cwd?: string;
22
23
  env?: NodeJS.ProcessEnv;
23
24
  nodeExecutable?: string;
25
+ /** Coherent executable/environment contract for an injected runtime. */
26
+ nodeRuntime?: NodeRuntimeLaunch;
24
27
  processEnv?: NodeJS.ProcessEnv;
25
28
  /** Shared Host only: retain process-owned managed login sources. */
26
29
  preserveManagedAuthenticationEnvironment?: boolean;
@@ -67,9 +70,10 @@ export declare function buildCodeBuddyCodeArgs(productLaunch?: ProductLaunchSnap
67
70
  * explicitly approved overrides are applied.
68
71
  */
69
72
  export declare function buildCodeBuddyCodeEnv(env?: NodeJS.ProcessEnv, processEnv?: NodeJS.ProcessEnv, options?: {
73
+ readonly nodeRuntime?: NodeRuntimeLaunch;
70
74
  readonly preserveManagedAuthenticationEnvironment?: boolean;
71
75
  }): NodeJS.ProcessEnv;
72
- export declare function createCodeBuddyCodeSpawnSpec({ binPath, cwd, env, nodeExecutable, processEnv, preserveManagedAuthenticationEnvironment, prewarmId, productLaunch, sessionEnabledPlugins, }?: CreateCodeBuddyCodeSpawnSpecOptions): CodeBuddyCodeSpawnSpec;
76
+ export declare function createCodeBuddyCodeSpawnSpec({ binPath, cwd, env, nodeExecutable, nodeRuntime, processEnv, preserveManagedAuthenticationEnvironment, prewarmId, productLaunch, sessionEnabledPlugins, }?: CreateCodeBuddyCodeSpawnSpecOptions): CodeBuddyCodeSpawnSpec;
73
77
  /**
74
78
  * Starts one private native-ACP-v2 CodeBuddy executor.
75
79
  *
@@ -46,6 +46,7 @@ const fs = __importStar(require("node:fs"));
46
46
  const path = __importStar(require("node:path"));
47
47
  const session_plugin_settings_js_1 = require("../protocol/session-plugin-settings.js");
48
48
  const client_identity_js_1 = require("./client-identity.js");
49
+ const node_runtime_js_1 = require("./node-runtime.js");
49
50
  const product_launch_js_1 = require("./product-launch.js");
50
51
  exports.CODEBUDDY_CODE_PACKAGE_NAME = '@tencent-ai/codebuddy-code';
51
52
  /** Package manifest for the CodeBuddy runtime embedded in this Harness build. */
@@ -181,16 +182,24 @@ function buildCodeBuddyCodeEnv(env = {}, processEnv = process.env, options = {})
181
182
  // them after this fallback-disable switch.
182
183
  result.CODEBUDDY_API_KEY_DISABLED = '1';
183
184
  }
184
- return result;
185
+ return (0, node_runtime_js_1.applyNodeRuntimeEnvironment)(result, options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)());
185
186
  }
186
- function createCodeBuddyCodeSpawnSpec({ binPath = resolveCodeBuddyCodeBinPath(), cwd, env, nodeExecutable = process.execPath, processEnv, preserveManagedAuthenticationEnvironment, prewarmId, productLaunch, sessionEnabledPlugins, } = {}) {
187
+ function createCodeBuddyCodeSpawnSpec({ binPath = resolveCodeBuddyCodeBinPath(), cwd, env, nodeExecutable, nodeRuntime, processEnv, preserveManagedAuthenticationEnvironment, prewarmId, productLaunch, sessionEnabledPlugins, } = {}) {
187
188
  if (prewarmId !== undefined
188
189
  && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(prewarmId)) {
189
190
  throw new Error('Private CodeBuddy prewarm id is invalid');
190
191
  }
191
192
  const resolvedProductLaunch = resolveProductLaunch(productLaunch);
193
+ if (nodeExecutable !== undefined
194
+ && nodeRuntime !== undefined
195
+ && nodeExecutable !== nodeRuntime.executable) {
196
+ throw new Error('CodeBuddy Node runtime executable is ambiguous');
197
+ }
198
+ const runtime = nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
199
+ ...(nodeExecutable === undefined ? {} : { executable: nodeExecutable }),
200
+ });
192
201
  return {
193
- command: nodeExecutable,
202
+ command: runtime.executable,
194
203
  args: [
195
204
  binPath,
196
205
  ...buildCodeBuddyCodeArgs(resolvedProductLaunch, sessionEnabledPlugins),
@@ -200,6 +209,7 @@ function createCodeBuddyCodeSpawnSpec({ binPath = resolveCodeBuddyCodeBinPath(),
200
209
  ],
201
210
  cwd,
202
211
  env: buildCodeBuddyCodeEnv(env, processEnv, {
212
+ nodeRuntime: runtime,
203
213
  preserveManagedAuthenticationEnvironment,
204
214
  }),
205
215
  };
@@ -1,6 +1,7 @@
1
1
  import { type SessionEnabledPlugins } from '../protocol/session-plugin-settings.js';
2
2
  import type { AgentPrewarmObserver } from './codebuddy-prewarm-observability.js';
3
3
  import type { CodeBuddyProcessSupervisor, CodeBuddyProcessSupervisorOptions } from './codebuddy-process.js';
4
+ import { type NodeRuntimeLaunch } from './node-runtime.js';
4
5
  import type { CodeBuddyProcessContainment } from './process-containment.js';
5
6
  import type { ProductLaunchSnapshot } from './product-manager.js';
6
7
  export interface CodeBuddyPrewarmKey {
@@ -19,6 +20,7 @@ export interface CreateCodeBuddyPrewarmKeyOptions {
19
20
  readonly cwd: string;
20
21
  readonly env?: NodeJS.ProcessEnv;
21
22
  readonly nodeExecutablePath?: string;
23
+ readonly nodeRuntime?: NodeRuntimeLaunch;
22
24
  readonly preserveManagedAuthenticationEnvironment?: boolean;
23
25
  readonly processEnv?: NodeJS.ProcessEnv;
24
26
  readonly productLaunch: ProductLaunchSnapshot;
@@ -41,6 +41,7 @@ const node_crypto_1 = require("node:crypto");
41
41
  const path = __importStar(require("node:path"));
42
42
  const session_plugin_settings_js_1 = require("../protocol/session-plugin-settings.js");
43
43
  const codebuddy_code_js_1 = require("./codebuddy-code.js");
44
+ const node_runtime_js_1 = require("./node-runtime.js");
44
45
  function createCodeBuddyPrewarmKey(options) {
45
46
  if (!path.isAbsolute(options.cliArtifactPath)) {
46
47
  throw new Error('Private CodeBuddy CLI artifact path must be absolute');
@@ -51,12 +52,18 @@ function createCodeBuddyPrewarmKey(options) {
51
52
  if (options.cliVersion.trim().length === 0 || options.cliVersion.length > 256) {
52
53
  throw new Error('Private CodeBuddy CLI version is invalid');
53
54
  }
55
+ const nodeRuntime = options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
56
+ ...(options.nodeExecutablePath === undefined
57
+ ? {}
58
+ : { executable: options.nodeExecutablePath }),
59
+ });
54
60
  const key = {
55
61
  canonicalCwd: path.resolve(options.cwd),
56
62
  cliArtifactPath: path.resolve(options.cliArtifactPath),
57
63
  cliVersion: options.cliVersion,
58
- nodeExecutablePath: path.resolve(options.nodeExecutablePath ?? process.execPath),
64
+ nodeExecutablePath: path.resolve(nodeRuntime.executable),
59
65
  preImportEnvironmentFingerprint: fingerprintEnvironment((0, codebuddy_code_js_1.buildCodeBuddyCodeEnv)(options.env, options.processEnv, {
66
+ nodeRuntime,
60
67
  preserveManagedAuthenticationEnvironment: options.preserveManagedAuthenticationEnvironment,
61
68
  })),
62
69
  productBootstrapFile: path.resolve(options.productLaunch.bootstrapFile),
@@ -112,6 +119,7 @@ function assertPrimeMatchesKey(key, options) {
112
119
  cwd: options.cwd,
113
120
  env: options.env,
114
121
  nodeExecutablePath: options.nodeExecutable,
122
+ nodeRuntime: options.nodeRuntime,
115
123
  preserveManagedAuthenticationEnvironment: options.preserveManagedAuthenticationEnvironment,
116
124
  processEnv: options.processEnv,
117
125
  productLaunch: options.productLaunch,
@@ -71,7 +71,8 @@ export interface CodeBuddyProcessSupervisorOptions extends Omit<SpawnCodeBuddyCo
71
71
  */
72
72
  parentDeathGuard?: boolean;
73
73
  /**
74
- * `process-tree` also tracks forkpty/setsid descendants through `ps`.
74
+ * `process-tree` also tracks forkpty/setsid descendants through an OS
75
+ * process snapshot.
75
76
  * Use `process-group` only for runtimes that cannot create detached
76
77
  * sessions, so cleanup can rely entirely on the detached child group.
77
78
  */
@@ -41,6 +41,7 @@ const stdio_guardian_js_1 = require("../integration/stdio-guardian.js");
41
41
  const errors_js_1 = require("../internal/errors.js");
42
42
  const codebuddy_code_js_1 = require("./codebuddy-code.js");
43
43
  const posix_contained_process_js_1 = require("./posix-contained-process.js");
44
+ const posix_process_snapshot_js_1 = require("./posix-process-snapshot.js");
44
45
  const process_containment_js_1 = require("./process-containment.js");
45
46
  const process_tree_js_1 = require("./process-tree.js");
46
47
  const DEFAULT_STDERR_TAIL_BYTES = 64 * 1024;
@@ -259,25 +260,6 @@ function createCodeBuddyNdJsonStream(output, input, maximumLineBytes, onInputFai
259
260
  });
260
261
  return { readable, writable };
261
262
  }
262
- function runProcessUtility(command, args, timeoutMs) {
263
- if (timeoutMs <= 0) {
264
- return Promise.reject(new Error(`${command} process utility deadline expired`));
265
- }
266
- return new Promise((resolve, reject) => {
267
- (0, node_child_process_1.execFile)(command, [...args], {
268
- encoding: 'utf8',
269
- maxBuffer: 1024 * 1024,
270
- timeout: Math.max(1, Math.min(500, timeoutMs)),
271
- windowsHide: true,
272
- }, (error, stdout) => {
273
- if (error) {
274
- reject(error);
275
- return;
276
- }
277
- resolve(typeof stdout === 'string' ? stdout : '');
278
- });
279
- });
280
- }
281
263
  function terminateWindowsProcessTree(rootPid, force, timeoutMs = 2_000) {
282
264
  if (process.platform !== 'win32') {
283
265
  return Promise.resolve(false);
@@ -303,9 +285,10 @@ async function listOwnedProcessTree(rootPid, platform, timeoutMs = 500) {
303
285
  if (platform === 'win32') {
304
286
  return [];
305
287
  }
306
- const spec = (0, process_tree_js_1.buildPosixProcessListSpec)(platform);
307
- const output = await runProcessUtility(spec.command, spec.args, timeoutMs);
308
- return (0, process_tree_js_1.collectPosixOwnedProcessIds)(output, rootPid, process.pid);
288
+ return (0, posix_process_snapshot_js_1.capturePosixOwnedProcessIds)(rootPid, platform, {
289
+ excludedPid: process.pid,
290
+ timeoutMs,
291
+ });
309
292
  }
310
293
  function requirePipe(value, name) {
311
294
  if (!value) {
@@ -12,6 +12,7 @@ import type { RuntimeLifecycleRecorder } from '../telemetry/index.js';
12
12
  import type { CodeBuddyProcessTimeouts } from './codebuddy-process.js';
13
13
  import { type CodeBuddyResourceProcessDependencies, CodeBuddyResourceProcessSupervisor, type CodeBuddyResourceProcessSupervisorOptions } from './codebuddy-resource-process.js';
14
14
  import { type CodeBuddyResourceRecoveryFuseOptions } from './codebuddy-resource-recovery.js';
15
+ import { type NodeRuntimeLaunch } from './node-runtime.js';
15
16
  import { CodeBuddyProcessContainment } from './process-containment.js';
16
17
  import type { ProductLaunchSnapshot } from './product-manager.js';
17
18
  export interface CreateCodeBuddyHostClientOptions {
@@ -22,6 +23,7 @@ export interface CreateCodeBuddyHostClientOptions {
22
23
  /** @deprecated Resource management no longer performs HTTP through fetch. */
23
24
  fetchImpl?: typeof fetch;
24
25
  nodeExecutable?: string;
26
+ nodeRuntime?: NodeRuntimeLaunch;
25
27
  processDependencies?: CodeBuddyResourceProcessDependencies;
26
28
  processEntryPath?: string;
27
29
  processFactory?: (options: CodeBuddyResourceProcessSupervisorOptions) => CodeBuddyResourceProcessSupervisor;
@@ -58,6 +58,7 @@ const codebuddy_code_js_1 = require("./codebuddy-code.js");
58
58
  const codebuddy_resource_process_js_1 = require("./codebuddy-resource-process.js");
59
59
  const codebuddy_resource_process_protocol_js_1 = require("./codebuddy-resource-process-protocol.js");
60
60
  const codebuddy_resource_recovery_js_1 = require("./codebuddy-resource-recovery.js");
61
+ const node_runtime_js_1 = require("./node-runtime.js");
61
62
  const process_containment_js_1 = require("./process-containment.js");
62
63
  const product_launch_js_1 = require("./product-launch.js");
63
64
  const DEFAULT_READY_TIMEOUT_MS = 60_000;
@@ -2571,6 +2572,16 @@ class CodeBuddyHostClientImpl {
2571
2572
  const password = (0, node_crypto_1.randomBytes)(24).toString('base64url');
2572
2573
  const runtimeInstanceId = (0, node_crypto_1.randomUUID)();
2573
2574
  const productLaunch = this.resolveProductLaunch();
2575
+ if (this.options.nodeExecutable !== undefined
2576
+ && this.options.nodeRuntime !== undefined
2577
+ && this.options.nodeExecutable !== this.options.nodeRuntime.executable) {
2578
+ throw new Error('CodeBuddy Host Node runtime executable is ambiguous');
2579
+ }
2580
+ const nodeRuntime = this.options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
2581
+ ...(this.options.nodeExecutable === undefined
2582
+ ? {}
2583
+ : { executable: this.options.nodeExecutable }),
2584
+ });
2574
2585
  const socket = await createCodeBuddyResourceSocket(platform);
2575
2586
  const environment = (0, codebuddy_code_js_1.buildCodeBuddyCodeEnv)({
2576
2587
  ...this.options.env,
@@ -2580,6 +2591,7 @@ class CodeBuddyHostClientImpl {
2580
2591
  CODEBUDDY_RUNTIME_INSTANCE_ID: runtimeInstanceId,
2581
2592
  DISABLE_AUTOUPDATER: 'true',
2582
2593
  }, this.options.processEnv, {
2594
+ nodeRuntime,
2583
2595
  preserveManagedAuthenticationEnvironment: true,
2584
2596
  });
2585
2597
  const binPath = this.options.binPath ?? (0, codebuddy_code_js_1.resolveCodeBuddyCodeBinPath)();
@@ -2595,7 +2607,7 @@ class CodeBuddyHostClientImpl {
2595
2607
  containment: this.containment,
2596
2608
  cwd: this.cwd,
2597
2609
  dependencies: this.options.processDependencies,
2598
- nodeExecutable: this.options.nodeExecutable,
2610
+ nodeRuntime,
2599
2611
  processEnv: this.options.processEnv,
2600
2612
  processRuntime: this.processRuntime,
2601
2613
  recordLifecycle: this.options.recordLifecycle,
@@ -4,6 +4,7 @@ import type { HarnessProcessRuntimeHandle } from '../effect-runtime/process-runt
4
4
  import { type PosixStdioMcpGuardian, type PosixStdioMcpGuardianOptions } from '../integration/stdio-guardian.js';
5
5
  import type { RuntimeLifecycleRecorder } from '../telemetry/index.js';
6
6
  import { type CodeBuddyResourceProcessBootstrapV1, type CodeBuddyResourceProcessRequest } from './codebuddy-resource-process-protocol.js';
7
+ import { type NodeRuntimeLaunch } from './node-runtime.js';
7
8
  import { CodeBuddyProcessContainment } from './process-containment.js';
8
9
  import { type ResolveWindowsJobLauncherPathOptions } from './windows-job.js';
9
10
  export type CodeBuddyResourceProcessState = 'idle' | 'starting' | 'running' | 'closing' | 'closed' | 'failed';
@@ -29,6 +30,7 @@ export interface CodeBuddyResourceProcessSupervisorOptions {
29
30
  readonly cwd: string;
30
31
  readonly dependencies?: CodeBuddyResourceProcessDependencies;
31
32
  readonly nodeExecutable?: string;
33
+ readonly nodeRuntime?: NodeRuntimeLaunch;
32
34
  /** Ambient system environment used only for the inert pre-containment stub. */
33
35
  readonly processEnv?: NodeJS.ProcessEnv;
34
36
  /** @internal Process-owned runtime for this child Scope. */
@@ -44,7 +46,7 @@ export interface CodeBuddyResourceWindowsJobSpawnSpec {
44
46
  readonly command: string;
45
47
  readonly options: SpawnOptions;
46
48
  }
47
- export declare function createCodeBuddyResourceStubEnvironment(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
49
+ export declare function createCodeBuddyResourceStubEnvironment(source?: NodeJS.ProcessEnv, nodeRuntime?: NodeRuntimeLaunch): NodeJS.ProcessEnv;
48
50
  /**
49
51
  * Wraps one inert Resource stub in a strict nested Job while retaining Node's
50
52
  * inherited IPC descriptor. The returned ChildProcess represents the native
@@ -54,6 +56,7 @@ export declare function createCodeBuddyResourceWindowsJobSpawnSpec(childEntryPat
54
56
  readonly cwd: string;
55
57
  readonly environment: NodeJS.ProcessEnv;
56
58
  readonly nodeExecutable?: string;
59
+ readonly nodeRuntime?: NodeRuntimeLaunch;
57
60
  }, launcherOptions?: ResolveWindowsJobLauncherPathOptions): CodeBuddyResourceWindowsJobSpawnSpec;
58
61
  export declare class CodeBuddyResourceProcessSupervisor extends EventEmitter {
59
62
  private readonly bootstrap;
@@ -70,7 +73,7 @@ export declare class CodeBuddyResourceProcessSupervisor extends EventEmitter {
70
73
  private failure?;
71
74
  private guardian?;
72
75
  private guardianPromise?;
73
- private readonly nodeExecutable;
76
+ private readonly nodeRuntime;
74
77
  private readonly pendingShutdownIds;
75
78
  private readonly processEnv;
76
79
  private readonly processRuntime;
@@ -43,6 +43,7 @@ const path = __importStar(require("node:path"));
43
43
  const stdio_guardian_js_1 = require("../integration/stdio-guardian.js");
44
44
  const errors_js_1 = require("../internal/errors.js");
45
45
  const codebuddy_resource_process_protocol_js_1 = require("./codebuddy-resource-process-protocol.js");
46
+ const node_runtime_js_1 = require("./node-runtime.js");
46
47
  const process_containment_js_1 = require("./process-containment.js");
47
48
  const process_tree_js_1 = require("./process-tree.js");
48
49
  const windows_job_js_1 = require("./windows-job.js");
@@ -71,7 +72,7 @@ const STUB_ENVIRONMENT_ALLOWLIST = Object.freeze([
71
72
  'https_proxy',
72
73
  'no_proxy',
73
74
  ]);
74
- function createCodeBuddyResourceStubEnvironment(source = process.env) {
75
+ function createCodeBuddyResourceStubEnvironment(source = process.env, nodeRuntime = (0, node_runtime_js_1.resolveNodeRuntimeLaunch)()) {
75
76
  const environment = {
76
77
  [RESOURCE_CHILD_MARKER]: '1',
77
78
  };
@@ -81,7 +82,7 @@ function createCodeBuddyResourceStubEnvironment(source = process.env) {
81
82
  environment[name] = value;
82
83
  }
83
84
  }
84
- return environment;
85
+ return (0, node_runtime_js_1.applyNodeRuntimeEnvironment)(environment, nodeRuntime);
85
86
  }
86
87
  /**
87
88
  * Wraps one inert Resource stub in a strict nested Job while retaining Node's
@@ -89,16 +90,26 @@ function createCodeBuddyResourceStubEnvironment(source = process.env) {
89
90
  * Job owner; lifecycle messages travel directly between Harness and target.
90
91
  */
91
92
  function createCodeBuddyResourceWindowsJobSpawnSpec(childEntryPath, options, launcherOptions = {}) {
93
+ if (options.nodeExecutable !== undefined
94
+ && options.nodeRuntime !== undefined
95
+ && options.nodeExecutable !== options.nodeRuntime.executable) {
96
+ throw new Error('CodeBuddy Resource Node runtime executable is ambiguous');
97
+ }
98
+ const nodeRuntime = options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
99
+ ...(options.nodeExecutable === undefined
100
+ ? {}
101
+ : { executable: options.nodeExecutable }),
102
+ });
92
103
  const launch = (0, windows_job_js_1.createWindowsJobChildLaunchSpec)({
93
104
  args: [childEntryPath],
94
- command: options.nodeExecutable ?? process.execPath,
105
+ command: nodeRuntime.executable,
95
106
  ...launcherOptions,
96
107
  });
97
108
  return {
98
109
  ...launch,
99
110
  options: {
100
111
  cwd: options.cwd,
101
- env: options.environment,
112
+ env: (0, node_runtime_js_1.applyNodeRuntimeEnvironment)(options.environment, nodeRuntime),
102
113
  serialization: 'json',
103
114
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
104
115
  windowsHide: true,
@@ -206,7 +217,16 @@ class CodeBuddyResourceProcessSupervisor extends node_events_1.EventEmitter {
206
217
  spawnChild: options.dependencies?.spawnChild
207
218
  ?? ((command, args, spawnOptions) => (0, node_child_process_1.spawn)(command, [...args], spawnOptions)),
208
219
  };
209
- this.nodeExecutable = options.nodeExecutable ?? process.execPath;
220
+ if (options.nodeExecutable !== undefined
221
+ && options.nodeRuntime !== undefined
222
+ && options.nodeExecutable !== options.nodeRuntime.executable) {
223
+ throw new Error('CodeBuddy Resource Node runtime executable is ambiguous');
224
+ }
225
+ this.nodeRuntime = options.nodeRuntime ?? (0, node_runtime_js_1.resolveNodeRuntimeLaunch)({
226
+ ...(options.nodeExecutable === undefined
227
+ ? {}
228
+ : { executable: options.nodeExecutable }),
229
+ });
210
230
  this.processEnv = options.processEnv ?? process.env;
211
231
  this.processRuntime = options.processRuntime;
212
232
  this.recordLifecycleObserver = options.recordLifecycle;
@@ -300,7 +320,7 @@ class CodeBuddyResourceProcessSupervisor extends node_events_1.EventEmitter {
300
320
  }
301
321
  const { openResourceProcessChildScope } = await import('../effect-runtime/resource-process-scope.mjs');
302
322
  this.assertStartupOpen();
303
- const stubEnvironment = createCodeBuddyResourceStubEnvironment(this.processEnv);
323
+ const stubEnvironment = createCodeBuddyResourceStubEnvironment(this.processEnv, this.nodeRuntime);
304
324
  try {
305
325
  const child = this.spawnStub(stubEnvironment);
306
326
  this.child = child;
@@ -451,11 +471,11 @@ class CodeBuddyResourceProcessSupervisor extends node_events_1.EventEmitter {
451
471
  const spec = createCodeBuddyResourceWindowsJobSpawnSpec(this.childEntryPath, {
452
472
  cwd: this.cwd,
453
473
  environment,
454
- nodeExecutable: this.nodeExecutable,
474
+ nodeRuntime: this.nodeRuntime,
455
475
  }, this.windowsLauncher);
456
476
  return this.dependencies.spawnChild(spec.command, spec.args, spec.options);
457
477
  }
458
- return this.dependencies.spawnChild(this.nodeExecutable, [this.childEntryPath], {
478
+ return this.dependencies.spawnChild(this.nodeRuntime.executable, [this.childEntryPath], {
459
479
  cwd: this.cwd,
460
480
  detached: true,
461
481
  env: environment,