@themoltnet/pi-runtime 0.7.3 → 0.8.0

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.
package/dist/index.d.ts CHANGED
@@ -53,6 +53,18 @@ export declare interface AllowedToolsClient {
53
53
  };
54
54
  }
55
55
 
56
+ export declare function assertGuestEnvironmentBoundary(options: {
57
+ guestCredentialMode: GuestCredentialMode;
58
+ forwardEnv?: readonly string[];
59
+ sandboxEnv?: Readonly<Record<string, string>>;
60
+ }): void;
61
+
62
+ /** @deprecated Prefer assertGuestEnvironmentBoundary for mode-aware checks. */
63
+ export declare function assertHostAuthenticatedGuestEnvironment(options: {
64
+ forwardEnv?: readonly string[];
65
+ sandboxEnv?: Readonly<Record<string, string>>;
66
+ }): void;
67
+
56
68
  /**
57
69
  * Construct an `AgentSession`. By default it is in-memory; callers may opt
58
70
  * parent sessions into daemon-owned file persistence via `sessionPersistence`.
@@ -390,6 +402,10 @@ export declare function executePiTask(claimedTask: ClaimedTask, reporter: TaskRe
390
402
  export declare interface ExecutePiTaskOptions {
391
403
  /** MoltNet agent whose credentials the VM boots with. */
392
404
  agentName: string;
405
+ /** Already-authenticated host-side Agent. Daemon callers always supply it. */
406
+ moltnetAgent?: Agent;
407
+ /** Explicit trust boundary for MoltNet credentials inside the guest. */
408
+ guestCredentialMode?: GuestCredentialMode;
393
409
  /**
394
410
  * Host root that owns `.moltnet/<agentName>/`.
395
411
  *
@@ -447,6 +463,8 @@ export declare interface ExecutePiTaskOptions {
447
463
  promptExtras?: Record<string, unknown>;
448
464
  /** Snapshot progress callback; defaults to stderr logging. */
449
465
  onSnapshotProgress?: (message: string) => void;
466
+ /** Structured VM credential-boundary diagnostics. */
467
+ onVmDiagnostic?: (diagnostic: VmDiagnostic) => void;
450
468
  /**
451
469
  * Optional pre-resolved checkpoint path. If omitted, `ensureSnapshot` is
452
470
  * invoked. Useful for batch execution where the caller wants to cache
@@ -642,6 +660,13 @@ export declare interface GondolinTemplateResolveContext {
642
660
  onProgress?: (message: string) => void;
643
661
  }
644
662
 
663
+ export declare type GuestCredentialMode = 'guest-config' | 'host-authenticated';
664
+
665
+ export declare class GuestEnvironmentBoundaryError extends Error {
666
+ readonly refusedNames: readonly string[];
667
+ constructor(refusedNames: readonly string[]);
668
+ }
669
+
645
670
  /**
646
671
  * Baseline env keys forwarded to host-exec child processes.
647
672
  * Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
@@ -703,7 +728,7 @@ export declare function isResolvedPathInsideRoot(path: string, root: string): bo
703
728
 
704
729
  export declare function isToolVisible(name: string, policy?: ModelVisibleToolPolicy): boolean;
705
730
 
706
- export declare function loadCredentials(agentDir: string): VmCredentials;
731
+ export declare function loadCredentials(agentDir: string, mode?: GuestCredentialMode, onDiagnostic?: (diagnostic: VmDiagnostic) => void): VmCredentials;
707
732
 
708
733
  export declare interface ManagedVm {
709
734
  vm: VM;
@@ -1076,6 +1101,8 @@ export declare interface ResolvedGondolinTemplate {
1076
1101
  resumeCommands: readonly ResumeCommand[];
1077
1102
  }
1078
1103
 
1104
+ export declare function resolveHostExecBaseEnv(guestCredentialMode: GuestCredentialMode, agentEnv: Readonly<Record<string, string | undefined>>): Set<string>;
1105
+
1079
1106
  /**
1080
1107
  * Resolve the session's tool policy at start-up.
1081
1108
  *
@@ -1366,6 +1393,12 @@ export declare interface VmConfig {
1366
1393
  checkpointPath: string;
1367
1394
  /** MoltNet agent name (used to resolve credentials). */
1368
1395
  agentName: string;
1396
+ /**
1397
+ * Trust boundary for guest credentials. `guest-config` injects the complete
1398
+ * legacy agent directory. `host-authenticated` never reads or injects it and
1399
+ * relies exclusively on the supplied host-side Agent for MoltNet operations.
1400
+ */
1401
+ guestCredentialMode?: GuestCredentialMode;
1369
1402
  /**
1370
1403
  * Host root that owns `.moltnet/<agentName>/`.
1371
1404
  *
@@ -1389,12 +1422,16 @@ export declare interface VmConfig {
1389
1422
  * into the guest without storing secret values in the profile.
1390
1423
  */
1391
1424
  forwardEnv?: string[];
1425
+ /** Structured credential-boundary diagnostics for daemon loggers. */
1426
+ onDiagnostic?: (diagnostic: VmDiagnostic) => void;
1392
1427
  /** Abort resume/setup work, closing any live VM owned by resumeVm. */
1393
1428
  signal?: AbortSignal;
1394
1429
  }
1395
1430
 
1396
1431
  export declare interface VmCredentials {
1432
+ /** Empty in host-authenticated mode; retained as strings for API stability. */
1397
1433
  moltnetJson: string;
1434
+ /** Empty in host-authenticated mode; retained as strings for API stability. */
1398
1435
  agentEnvRaw: string;
1399
1436
  /**
1400
1437
  * Pi OAuth/API-key auth blob. Null when neither `~/.pi/agent/auth.json`
@@ -1415,6 +1452,13 @@ export declare interface VmCredentials {
1415
1452
  githubAppPemFilename: string | null;
1416
1453
  }
1417
1454
 
1455
+ export declare interface VmDiagnostic {
1456
+ event: 'vm.credentials.mode' | 'vm.credentials.github_key_missing';
1457
+ level: 'info' | 'warning';
1458
+ message: string;
1459
+ credentialMode: GuestCredentialMode;
1460
+ }
1461
+
1418
1462
  /**
1419
1463
  * Subset of `@earendil-works/gondolin`'s `VmFs` we actually use. We
1420
1464
  * narrow the dependency surface so unit tests can hand in a
package/dist/index.js CHANGED
@@ -4532,12 +4532,30 @@ function resolveVmAgentDir(config) {
4532
4532
  const rootDir = config.agentRootDir ?? findMainWorktree();
4533
4533
  return path.join(rootDir, ".moltnet", config.agentName);
4534
4534
  }
4535
- function loadCredentials(agentDir) {
4536
- const moltnetJson = readFileSync(path.join(agentDir, "moltnet.json"), "utf8");
4537
- const agentEnvRaw = readFileSync(path.join(agentDir, "env"), "utf8");
4535
+ function loadCredentials(agentDir, mode = "guest-config", onDiagnostic) {
4536
+ const moltnetPath = path.join(agentDir, "moltnet.json");
4537
+ const agentEnvPath = path.join(agentDir, "env");
4538
4538
  const piAgentDir = resolvePiCodingAgentDir();
4539
4539
  const piAuthPath = path.join(piAgentDir, "auth.json");
4540
4540
  const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
4541
+ if (mode === "host-authenticated") return {
4542
+ moltnetJson: "",
4543
+ agentEnvRaw: "",
4544
+ piAuthJson,
4545
+ agentEnv: {},
4546
+ gitconfig: null,
4547
+ sshPrivateKey: null,
4548
+ sshPublicKey: null,
4549
+ allowedSigners: null,
4550
+ githubAppPem: null,
4551
+ githubAppPemFilename: null
4552
+ };
4553
+ const hasMoltnetJson = existsSync(moltnetPath);
4554
+ const hasAgentEnv = existsSync(agentEnvPath);
4555
+ if (!hasMoltnetJson || !hasAgentEnv) throw new Error(`Guest credential mode requires both ${moltnetPath} and ${agentEnvPath}`);
4556
+ const moltnetJson = readFileSync(moltnetPath, "utf8");
4557
+ const agentEnvRaw = readFileSync(agentEnvPath, "utf8");
4558
+ if (moltnetJson.trim() === "") throw new Error(`Agent configuration is empty: ${moltnetPath}`);
4541
4559
  const gitconfigPath = path.join(agentDir, "gitconfig");
4542
4560
  const gitconfig = existsSync(gitconfigPath) ? readFileSync(gitconfigPath, "utf8") : null;
4543
4561
  const sshDir = path.join(agentDir, "ssh");
@@ -4546,9 +4564,13 @@ function loadCredentials(agentDir) {
4546
4564
  const allowedSigners = existsSync(path.join(sshDir, "allowed_signers")) ? readFileSync(path.join(sshDir, "allowed_signers"), "utf8") : null;
4547
4565
  let githubAppPem = null;
4548
4566
  let githubAppPemFilename = null;
4549
- const pemPath = JSON.parse(moltnetJson).github?.private_key_path;
4550
- if (pemPath) if (!existsSync(pemPath)) process.stderr.write(`[pi-extension] Warning: github.private_key_path not found at ${pemPath} — moltnet github token will fail inside the guest
4551
- `);
4567
+ const pemPath = (moltnetJson ? JSON.parse(moltnetJson) : null)?.github?.private_key_path;
4568
+ if (pemPath) if (!existsSync(pemPath)) onDiagnostic?.({
4569
+ event: "vm.credentials.github_key_missing",
4570
+ level: "warning",
4571
+ credentialMode: mode,
4572
+ message: `github.private_key_path not found at ${pemPath}; moltnet github token will fail inside the guest`
4573
+ });
4552
4574
  else {
4553
4575
  githubAppPem = readFileSync(pemPath, "utf8");
4554
4576
  githubAppPemFilename = path.basename(pemPath);
@@ -4594,6 +4616,70 @@ var BASE_ALLOWED_HOSTS = [
4594
4616
  "storage.googleapis.com",
4595
4617
  "*.googlesource.com"
4596
4618
  ];
4619
+ var DEFAULT_MOLTNET_API_URL = "https://api.themolt.net";
4620
+ /**
4621
+ * Host environment names that may intentionally cross into a
4622
+ * host-authenticated guest. This local list is the authority boundary;
4623
+ * server-supplied runtime-profile `requiredEnv` cannot widen it.
4624
+ */
4625
+ var HOST_AUTHENTICATED_GUEST_ENV_ALLOWLIST = new Set([
4626
+ "ANTHROPIC_API_KEY",
4627
+ "OPENAI_API_KEY",
4628
+ "OPENAI_BASE_URL",
4629
+ "AZURE_OPENAI_API_KEY",
4630
+ "AZURE_OPENAI_ENDPOINT",
4631
+ "AZURE_OPENAI_API_VERSION",
4632
+ "GOOGLE_API_KEY",
4633
+ "GEMINI_API_KEY",
4634
+ "MISTRAL_API_KEY",
4635
+ "GROQ_API_KEY",
4636
+ "OPENROUTER_API_KEY",
4637
+ "XAI_API_KEY",
4638
+ "CEREBRAS_API_KEY",
4639
+ "DEEPSEEK_API_KEY",
4640
+ "OLLAMA_API_KEY",
4641
+ "OLLAMA_BASE_URL",
4642
+ "AWS_ACCESS_KEY_ID",
4643
+ "AWS_SECRET_ACCESS_KEY",
4644
+ "AWS_SESSION_TOKEN",
4645
+ "AWS_REGION",
4646
+ "AWS_DEFAULT_REGION",
4647
+ "GITHUB_TOKEN",
4648
+ "GH_TOKEN",
4649
+ "LINEAR_API_KEY"
4650
+ ]);
4651
+ var RESERVED_GUEST_ENVIRONMENT_NAMES = new Set([
4652
+ "PATH",
4653
+ "HOME",
4654
+ "NODE_EXTRA_CA_CERTS",
4655
+ "MOLTNET_GUEST_WORKSPACE",
4656
+ "GIT_SSH",
4657
+ "GIT_SSH_COMMAND",
4658
+ "SSH_AUTH_SOCK"
4659
+ ]);
4660
+ function isReservedGuestEnvironmentName(name) {
4661
+ return name.startsWith("MOLTNET_") || name.startsWith("GIT_CONFIG_") || RESERVED_GUEST_ENVIRONMENT_NAMES.has(name);
4662
+ }
4663
+ var GuestEnvironmentBoundaryError = class extends Error {
4664
+ constructor(refusedNames) {
4665
+ super(`Guest credential boundary refuses runtime-controlled environment variables: ${refusedNames.join(", ")}. Remove them from the runtime profile; MoltNet operations use the trusted host-side Agent.`);
4666
+ this.refusedNames = refusedNames;
4667
+ this.name = "GuestEnvironmentBoundaryError";
4668
+ }
4669
+ };
4670
+ function assertGuestEnvironmentBoundary(options) {
4671
+ const refusedForwardEnv = (options.forwardEnv ?? []).filter((name) => isReservedGuestEnvironmentName(name) || options.guestCredentialMode === "host-authenticated" && !HOST_AUTHENTICATED_GUEST_ENV_ALLOWLIST.has(name));
4672
+ const refusedSandboxEnv = Object.keys(options.sandboxEnv ?? {}).filter(isReservedGuestEnvironmentName);
4673
+ const refused = [...new Set([...refusedForwardEnv, ...refusedSandboxEnv])].sort();
4674
+ if (refused.length > 0) throw new GuestEnvironmentBoundaryError(refused);
4675
+ }
4676
+ /** @deprecated Prefer assertGuestEnvironmentBoundary for mode-aware checks. */
4677
+ function assertHostAuthenticatedGuestEnvironment(options) {
4678
+ assertGuestEnvironmentBoundary({
4679
+ guestCredentialMode: "host-authenticated",
4680
+ ...options
4681
+ });
4682
+ }
4597
4683
  /**
4598
4684
  * Return whether two Gondolin hostname globs can match at least one common
4599
4685
  * string. Each `*` is an arbitrary substring, so this walks the product of the
@@ -4665,10 +4751,22 @@ async function resumeVm(config) {
4665
4751
  throwIfAborted(config.signal, "VM resume");
4666
4752
  const agentDir = resolveVmAgentDir(config);
4667
4753
  const guestWorkspace = path.resolve(config.mountPath);
4668
- if (!existsSync(agentDir)) throw new Error(`Agent directory not found: ${agentDir}. Run: moltnet register --name ${config.agentName}`);
4669
- const creds = loadCredentials(agentDir);
4670
- const moltnetConfig = JSON.parse(creds.moltnetJson);
4671
- const apiHost = new URL(moltnetConfig.endpoints.api).hostname;
4754
+ const guestCredentialMode = config.guestCredentialMode ?? "guest-config";
4755
+ if (guestCredentialMode === "guest-config" && !existsSync(agentDir)) throw new Error(`Agent directory not found: ${agentDir}. Run: moltnet register --name ${config.agentName}`);
4756
+ assertGuestEnvironmentBoundary({
4757
+ guestCredentialMode,
4758
+ forwardEnv: config.forwardEnv,
4759
+ sandboxEnv: config.sandboxConfig?.env
4760
+ });
4761
+ config.onDiagnostic?.({
4762
+ event: "vm.credentials.mode",
4763
+ level: "info",
4764
+ credentialMode: guestCredentialMode,
4765
+ message: guestCredentialMode === "host-authenticated" ? "MoltNet agent files and non-allowlisted host environment variables are withheld from the guest" : "The complete MoltNet agent configuration is available to the guest"
4766
+ });
4767
+ const creds = loadCredentials(agentDir, guestCredentialMode, config.onDiagnostic);
4768
+ const configuredApiUrl = creds.moltnetJson ? JSON.parse(creds.moltnetJson).endpoints.api : void 0;
4769
+ const apiHost = new URL(configuredApiUrl ?? process.env.MOLTNET_API_URL ?? DEFAULT_MOLTNET_API_URL).hostname;
4672
4770
  const runtimeAllowedHosts = config.sandboxConfig?.network?.allowedHosts ?? [];
4673
4771
  const runtimeAllowedInternalHosts = config.sandboxConfig?.network?.allowedInternalHosts ?? [];
4674
4772
  const protectedExternalHosts = [...new Set([
@@ -4689,7 +4787,7 @@ async function resumeVm(config) {
4689
4787
  else if (k.endsWith("_PRIVATE_KEY_PATH")) vmAgentEnv[k] = `${vmAgentDir}/${path.basename(v)}`;
4690
4788
  else vmAgentEnv[k] = v;
4691
4789
  }
4692
- vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
4790
+ if (creds.moltnetJson) vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
4693
4791
  const vfsConfig = resolveVfsShadowConfig(config.sandboxConfig);
4694
4792
  let workspaceProvider = new RealFSProvider(config.mountPath);
4695
4793
  workspaceProvider = new ShadowProvider(workspaceProvider, {
@@ -4705,6 +4803,11 @@ async function resumeVm(config) {
4705
4803
  writeMode: vfsConfig.mode
4706
4804
  });
4707
4805
  }
4806
+ if (guestCredentialMode === "host-authenticated") workspaceProvider = new ShadowProvider(workspaceProvider, {
4807
+ shouldShadow: ({ path: shadowPath }) => shadowPath.split("/").includes(".moltnet"),
4808
+ denySymlinkBypass: true,
4809
+ writeMode: "deny"
4810
+ });
4708
4811
  const forwardedEnv = {};
4709
4812
  for (const name of config.forwardEnv ?? []) {
4710
4813
  const value = process.env[name];
@@ -4777,44 +4880,47 @@ async function resumeVm(config) {
4777
4880
  if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(nonErrorMessage(lastErr));
4778
4881
  }
4779
4882
  const vmSshDir = `${vmAgentDir}/ssh`;
4780
- await vm.exec(`mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent`, { signal: config.signal });
4883
+ const hasAgentFiles = guestCredentialMode === "guest-config";
4884
+ await vm.exec(hasAgentFiles ? `mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent` : "mkdir -p /home/agent/.pi/agent", { signal: config.signal });
4781
4885
  if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, {
4782
4886
  mode: 384,
4783
4887
  signal: config.signal
4784
4888
  });
4785
- const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
4786
- await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
4787
- mode: 384,
4788
- signal: config.signal
4789
- });
4790
- await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, {
4791
- mode: 384,
4792
- signal: config.signal
4793
- });
4794
- if (creds.gitconfig) {
4795
- const vmGitconfig = rewriteGitconfigPaths(creds.gitconfig, vmSshDir, vmAgentDir);
4796
- await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
4889
+ if (hasAgentFiles) {
4890
+ const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
4891
+ await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
4892
+ mode: 384,
4893
+ signal: config.signal
4894
+ });
4895
+ await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, {
4896
+ mode: 384,
4897
+ signal: config.signal
4898
+ });
4899
+ if (creds.gitconfig) {
4900
+ const vmGitconfig = rewriteGitconfigPaths(creds.gitconfig, vmSshDir, vmAgentDir);
4901
+ await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
4902
+ mode: 420,
4903
+ signal: config.signal
4904
+ });
4905
+ }
4906
+ if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
4907
+ mode: 384,
4908
+ signal: config.signal
4909
+ });
4910
+ if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, {
4797
4911
  mode: 420,
4798
4912
  signal: config.signal
4799
4913
  });
4914
+ if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, {
4915
+ mode: 420,
4916
+ signal: config.signal
4917
+ });
4918
+ if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, {
4919
+ mode: 384,
4920
+ signal: config.signal
4921
+ });
4800
4922
  }
4801
- if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
4802
- mode: 384,
4803
- signal: config.signal
4804
- });
4805
- if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, {
4806
- mode: 420,
4807
- signal: config.signal
4808
- });
4809
- if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, {
4810
- mode: 420,
4811
- signal: config.signal
4812
- });
4813
- if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, {
4814
- mode: 384,
4815
- signal: config.signal
4816
- });
4817
- await vm.exec("chown -R agent:agent /home/agent/.pi /home/agent/.moltnet", { signal: config.signal });
4923
+ await vm.exec(hasAgentFiles ? "chown -R agent:agent /home/agent/.pi /home/agent/.moltnet" : "chown -R agent:agent /home/agent/.pi", { signal: config.signal });
4818
4924
  return {
4819
4925
  vm,
4820
4926
  credentials: creds,
@@ -7104,6 +7210,19 @@ var GONDOLIN_TOOL_NAMES = [
7104
7210
  "find",
7105
7211
  "grep"
7106
7212
  ];
7213
+ var HOST_AUTHENTICATED_HOST_EXEC_REFUSED_ENV = new Set([
7214
+ "GIT_CONFIG_GLOBAL",
7215
+ "MOLTNET_CREDENTIALS_PATH",
7216
+ "SSH_AUTH_SOCK"
7217
+ ]);
7218
+ function resolveHostExecBaseEnv(guestCredentialMode, agentEnv) {
7219
+ const names = new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(agentEnv)]);
7220
+ if (guestCredentialMode === "host-authenticated") {
7221
+ for (const name of HOST_AUTHENTICATED_HOST_EXEC_REFUSED_ENV) names.delete(name);
7222
+ for (const name of names) if (name.startsWith("MOLTNET_")) names.delete(name);
7223
+ }
7224
+ return names;
7225
+ }
7107
7226
  var noopTurnEventHandler = () => {};
7108
7227
  async function openVmWorkspaceFileForRead(config) {
7109
7228
  const localPath = isAbsolute(config.filePath) ? config.filePath : resolve(config.cwdPath, config.filePath);
@@ -7135,6 +7254,16 @@ function createGondolinToolDefinitions(config) {
7135
7254
  }
7136
7255
  ];
7137
7256
  }
7257
+ function createMoltNetAgentResolver(input) {
7258
+ let resolved;
7259
+ return () => {
7260
+ if (!resolved) resolved = (input.moltnetAgent ? Promise.resolve(input.moltnetAgent) : (input.connectAgent ?? ((configDir) => connect({ configDir })))(input.configDir)).catch((error) => {
7261
+ resolved = void 0;
7262
+ throw error;
7263
+ });
7264
+ return resolved;
7265
+ };
7266
+ }
7138
7267
  /**
7139
7268
  * Factory that builds a pi-specific `executeTask` function suitable for
7140
7269
  * injection into `AgentRuntime`. The returned function caches the resolved
@@ -7318,11 +7447,13 @@ async function executePiTask(claimedTask, reporter, opts) {
7318
7447
  checkpointPath,
7319
7448
  agentName: opts.agentName,
7320
7449
  agentRootDir,
7450
+ guestCredentialMode: opts.guestCredentialMode,
7321
7451
  mountPath,
7322
7452
  workspaceMode: preparedWorkspace.mode,
7323
7453
  extraAllowedHosts: opts.extraAllowedHosts,
7324
7454
  sandboxConfig: effectiveSandboxConfig,
7325
7455
  forwardEnv: opts.forwardEnv,
7456
+ onDiagnostic: opts.onVmDiagnostic,
7326
7457
  signal: reporter.cancelSignal
7327
7458
  }));
7328
7459
  } catch (err) {
@@ -7339,6 +7470,10 @@ async function executePiTask(claimedTask, reporter, opts) {
7339
7470
  activateAgentEnv(managed.credentials.agentEnv, agentRootDir);
7340
7471
  const activeWorkspace = preparedWorkspace;
7341
7472
  const activeManaged = managed;
7473
+ const getMoltNetAgent = createMoltNetAgentResolver({
7474
+ moltnetAgent: opts.moltnetAgent,
7475
+ configDir: managed.agentDir
7476
+ });
7342
7477
  await emit("info", {
7343
7478
  event: "execute_start",
7344
7479
  correlationId: task.correlationId ?? null,
@@ -7353,7 +7488,7 @@ async function executePiTask(claimedTask, reporter, opts) {
7353
7488
  let resolvedPriorContext;
7354
7489
  const continueFrom = task.input?.continueFrom;
7355
7490
  if (task.taskType === FREEFORM_TYPE && continueFrom) try {
7356
- const resolved = await resolvePriorContext(await connect({ configDir: managed.agentDir }), continueFrom);
7491
+ const resolved = await resolvePriorContext(await getMoltNetAgent(), continueFrom);
7357
7492
  if (resolved) {
7358
7493
  resolvedPriorContext = resolved;
7359
7494
  await emit("info", {
@@ -7468,7 +7603,7 @@ async function executePiTask(claimedTask, reporter, opts) {
7468
7603
  });
7469
7604
  const submitTools = submitToolDefs;
7470
7605
  try {
7471
- const moltnetAgent = await connect({ configDir: managed.agentDir });
7606
+ const moltnetAgent = await getMoltNetAgent();
7472
7607
  const moltnetTools = createMoltNetTools({
7473
7608
  getAgent: () => moltnetAgent,
7474
7609
  getDiaryId: () => diaryId,
@@ -7482,7 +7617,7 @@ async function executePiTask(claimedTask, reporter, opts) {
7482
7617
  guestWorkspace: activeManaged.guestWorkspace,
7483
7618
  filePath
7484
7619
  }),
7485
- hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
7620
+ hostExecBaseEnv: resolveHostExecBaseEnv(opts.guestCredentialMode ?? "guest-config", managed.credentials.agentEnv),
7486
7621
  hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
7487
7622
  getTaskContext: () => ({
7488
7623
  taskId: task.id,
@@ -8564,4 +8699,4 @@ function describeToolErrorMessage(result) {
8564
8699
  }
8565
8700
  }
8566
8701
  //#endregion
8567
- export { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION, PI_RUNTIME_DEFINITION_VERSION, activateAgentEnv, buildAgentSession, buildPiExecutorManifest, buildRuntimeKernel, buildWorkspaceMountInstructions, createGondolinBashOps, createGondolinEditOps, createGondolinFindOps, createGondolinLsOps, createGondolinReadOps, createGondolinToolDefinitions, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, createToolPolicyExtension, decideForEvent, decideToolCall, defineGondolinTemplate, definePiExtension, definePiRuntime, definePiTool, enabledPiToolNames, ensureSnapshot, executeGondolinGrep, executePiTask, filterModelVisibleTools, findMainWorktree, injectRuntimeContext as injectTaskContext, isKernelTool, isResolvedPathInsideRoot, isToolVisible, loadCredentials, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveSessionToolPolicy, resolveTaskWorktreePath, resumeVm, toGuestPath };
8702
+ export { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, GuestEnvironmentBoundaryError, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION, PI_RUNTIME_DEFINITION_VERSION, activateAgentEnv, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, buildAgentSession, buildPiExecutorManifest, buildRuntimeKernel, buildWorkspaceMountInstructions, createGondolinBashOps, createGondolinEditOps, createGondolinFindOps, createGondolinLsOps, createGondolinReadOps, createGondolinToolDefinitions, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, createToolPolicyExtension, decideForEvent, decideToolCall, defineGondolinTemplate, definePiExtension, definePiRuntime, definePiTool, enabledPiToolNames, ensureSnapshot, executeGondolinGrep, executePiTask, filterModelVisibleTools, findMainWorktree, injectRuntimeContext as injectTaskContext, isKernelTool, isResolvedPathInsideRoot, isToolVisible, loadCredentials, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveHostExecBaseEnv, resolveSessionToolPolicy, resolveTaskWorktreePath, resumeVm, toGuestPath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/pi-runtime",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Composable MoltNet runtime kernel for Pi agents in Gondolin VMs",
@@ -31,10 +31,10 @@
31
31
  "@opentelemetry/api": "^1.9.0",
32
32
  "multiformats": "^13.3.0",
33
33
  "typebox": "^1.2.8",
34
- "@themoltnet/agent-runtime": "0.41.2",
35
- "@themoltnet/sdk": "0.131.0",
36
- "@themoltnet/shell-command-analyzer": "0.3.0",
37
- "@themoltnet/os-keyring": "0.2.0"
34
+ "@themoltnet/agent-runtime": "0.41.3",
35
+ "@themoltnet/os-keyring": "0.2.0",
36
+ "@themoltnet/sdk": "0.132.0",
37
+ "@themoltnet/shell-command-analyzer": "0.3.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-ai": "0.79.4",
@@ -52,8 +52,8 @@
52
52
  "vite-plugin-dts": "^4.5.4",
53
53
  "vitest": "^3.0.0",
54
54
  "@moltnet/crypto-service": "0.1.0",
55
- "@moltnet/models": "0.1.0",
56
- "@moltnet/tasks": "0.1.0"
55
+ "@moltnet/tasks": "0.1.0",
56
+ "@moltnet/models": "0.1.0"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=24"