@themoltnet/pi-runtime 0.7.2 → 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 +45 -1
- package/dist/index.js +181 -46
- package/package.json +5 -4
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
|
@@ -19,7 +19,7 @@ import { createHash as createHash$1 } from "node:crypto";
|
|
|
19
19
|
import * as json from "multiformats/codecs/json";
|
|
20
20
|
import "@ipld/dag-cbor";
|
|
21
21
|
import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID, TaskContext, buildTaskUserPrompt, getSubmitOutputContract, materializeTaskOutput, mergeRuntimeProfileContext, resolveTaskContext, taskTypeUsesSubagents, traceRuntimePhase, validateTaskOutput, validateTaskSubmission } from "@themoltnet/agent-runtime";
|
|
22
|
-
import { connect } from "@themoltnet/sdk";
|
|
22
|
+
import { connect } from "@themoltnet/sdk/node";
|
|
23
23
|
import { ShellCommandAnalyzer } from "@themoltnet/shell-command-analyzer";
|
|
24
24
|
import { homedir } from "node:os";
|
|
25
25
|
import { MemoryProvider, RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, createShadowPathPredicate, ensureImageSelector, isWriteFlag, loadGuestAssets } from "@earendil-works/gondolin";
|
|
@@ -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
|
|
4537
|
-
const
|
|
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)
|
|
4550
|
-
if (pemPath) if (!existsSync(pemPath))
|
|
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
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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.
|
|
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,8 +31,9 @@
|
|
|
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.
|
|
35
|
-
"@themoltnet/
|
|
34
|
+
"@themoltnet/agent-runtime": "0.41.3",
|
|
35
|
+
"@themoltnet/os-keyring": "0.2.0",
|
|
36
|
+
"@themoltnet/sdk": "0.132.0",
|
|
36
37
|
"@themoltnet/shell-command-analyzer": "0.3.0"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
"vite": "^8.0.0",
|
|
51
52
|
"vite-plugin-dts": "^4.5.4",
|
|
52
53
|
"vitest": "^3.0.0",
|
|
53
|
-
"@moltnet/tasks": "0.1.0",
|
|
54
54
|
"@moltnet/crypto-service": "0.1.0",
|
|
55
|
+
"@moltnet/tasks": "0.1.0",
|
|
55
56
|
"@moltnet/models": "0.1.0"
|
|
56
57
|
},
|
|
57
58
|
"engines": {
|