@themoltnet/pi-runtime 0.12.0 → 0.12.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.
- package/dist/index.d.ts +37 -28
- package/dist/index.js +188 -98
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ import { FindOperations } from '@earendil-works/pi-coding-agent';
|
|
|
21
21
|
import { GONDOLIN_BASE_EXECUTABLES } from '@themoltnet/sandbox-gondolin';
|
|
22
22
|
import { GrepToolDetails } from '@earendil-works/pi-coding-agent';
|
|
23
23
|
import { GrepToolInput } from '@earendil-works/pi-coding-agent';
|
|
24
|
-
import { GuestCredentialMode } from '@themoltnet/sandbox-gondolin';
|
|
25
24
|
import { GuestEnvironmentBoundaryError } from '@themoltnet/sandbox-gondolin';
|
|
26
25
|
import { HostCapabilityContribution } from '@themoltnet/agent-runtime';
|
|
27
26
|
import { HostCapabilityEvidenceLogger } from '@themoltnet/agent-runtime';
|
|
@@ -34,7 +33,6 @@ import { ManagedVm } from '@themoltnet/sandbox-gondolin';
|
|
|
34
33
|
import { Model } from '@earendil-works/pi-ai';
|
|
35
34
|
import { ModelRegistry } from '@earendil-works/pi-coding-agent';
|
|
36
35
|
import { prepareBrokeredHttpSecrets } from '@themoltnet/sandbox-gondolin';
|
|
37
|
-
import { ProviderAuthSource } from '@themoltnet/sandbox-gondolin';
|
|
38
36
|
import { Readable } from 'node:stream';
|
|
39
37
|
import { ReadOperations } from '@earendil-works/pi-coding-agent';
|
|
40
38
|
import { ResumeCommand } from '@themoltnet/sandbox-gondolin';
|
|
@@ -272,7 +270,10 @@ declare type ContextRef = {
|
|
|
272
270
|
content: string;
|
|
273
271
|
};
|
|
274
272
|
|
|
275
|
-
export declare function createGondolinBashOps(vm: VM, localCwd: string, guestWorkspace: string
|
|
273
|
+
export declare function createGondolinBashOps(vm: VM, localCwd: string, guestWorkspace: string, config: {
|
|
274
|
+
lifecycle: GondolinToolLifecycle;
|
|
275
|
+
retireVm: (retirement: GondolinVmRetirement) => Promise<void>;
|
|
276
|
+
}): BashOperations;
|
|
276
277
|
|
|
277
278
|
export declare function createGondolinEditOps(vm: VM, localCwd: string, guestWorkspace: string): EditOperations;
|
|
278
279
|
|
|
@@ -286,8 +287,14 @@ export declare function createGondolinToolDefinitions(config: {
|
|
|
286
287
|
vm: VM;
|
|
287
288
|
cwdPath: string;
|
|
288
289
|
guestWorkspace: string;
|
|
290
|
+
lifecycle: GondolinToolLifecycle;
|
|
291
|
+
retireVm: (retirement: GondolinVmRetirement) => Promise<void>;
|
|
289
292
|
}): ToolDefinition[];
|
|
290
293
|
|
|
294
|
+
export declare function createGondolinToolLifecycle(config?: {
|
|
295
|
+
onRetired?: (retirement: GondolinVmRetirement) => void;
|
|
296
|
+
}): GondolinToolLifecycle;
|
|
297
|
+
|
|
291
298
|
export declare function createGondolinWriteOps(vm: VM, localCwd: string, guestWorkspace: string): WriteOperations;
|
|
292
299
|
|
|
293
300
|
/**
|
|
@@ -527,8 +534,6 @@ export declare interface ExecutePiTaskOptions {
|
|
|
527
534
|
agentName: string;
|
|
528
535
|
/** Already-authenticated host-side Agent. Daemon callers always supply it. */
|
|
529
536
|
moltnetAgent?: Agent;
|
|
530
|
-
/** Explicit trust boundary for MoltNet credentials inside the guest. */
|
|
531
|
-
guestCredentialMode?: GuestCredentialMode;
|
|
532
537
|
/**
|
|
533
538
|
* Host root that owns `.moltnet/<agentName>/`.
|
|
534
539
|
*
|
|
@@ -786,14 +791,34 @@ export declare interface GondolinTemplateResolveContext {
|
|
|
786
791
|
onProgress?: (message: string) => void;
|
|
787
792
|
}
|
|
788
793
|
|
|
794
|
+
export declare interface GondolinToolLifecycle {
|
|
795
|
+
assertActive(): void;
|
|
796
|
+
getRetirement(): GondolinVmRetirement | null;
|
|
797
|
+
markRetired(retirement: GondolinVmRetirement): void;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export declare class GondolinVmRetiredError extends Error {
|
|
801
|
+
readonly retirement: GondolinVmRetirement;
|
|
802
|
+
readonly code = "sandbox_retired";
|
|
803
|
+
constructor(retirement: GondolinVmRetirement, options?: ErrorOptions);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export declare interface GondolinVmRetirement {
|
|
807
|
+
backendRetired: boolean;
|
|
808
|
+
reason: 'backend-retired' | 'backend-retirement-failed';
|
|
809
|
+
trigger: GondolinVmRetirementTrigger;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export declare type GondolinVmRetirementTrigger = 'cancellation' | 'timeout';
|
|
813
|
+
|
|
814
|
+
export declare function guardGondolinToolDefinitions(tools: readonly ToolDefinition[], lifecycle: GondolinToolLifecycle): ToolDefinition[];
|
|
815
|
+
|
|
789
816
|
export declare const GUEST_ALLOWED_SIGNERS_PATH = "/home/agent/.config/moltnet/allowed_signers";
|
|
790
817
|
|
|
791
818
|
export declare const GUEST_GITCONFIG_PATH = "/home/agent/.config/moltnet/gitconfig";
|
|
792
819
|
|
|
793
820
|
export declare const GUEST_SIGNER_SOCKET = "/run/moltnet/signer.sock";
|
|
794
821
|
|
|
795
|
-
export { GuestCredentialMode }
|
|
796
|
-
|
|
797
822
|
export { GuestEnvironmentBoundaryError }
|
|
798
823
|
|
|
799
824
|
/**
|
|
@@ -989,9 +1014,6 @@ export declare function normalizeRetryTriageResult(value: unknown): PiRetryTriag
|
|
|
989
1014
|
|
|
990
1015
|
export declare const PI_EXECUTOR_MANIFEST_VERSION: "moltnet:executor-manifest:v1";
|
|
991
1016
|
|
|
992
|
-
/** Guest path where Pi expects its auth blob. */
|
|
993
|
-
export declare const PI_GUEST_AUTH_PATH = "/home/agent/.pi/agent/auth.json";
|
|
994
|
-
|
|
995
1017
|
export declare const PI_RUNTIME_DEFINITION_VERSION = "moltnet:pi-runtime:v1";
|
|
996
1018
|
|
|
997
1019
|
export declare interface PiBrokeredHttpSecretContribution {
|
|
@@ -1092,13 +1114,6 @@ export declare interface PiOtelOptions {
|
|
|
1092
1114
|
onSessionContextChange?: (context: Context | undefined) => void;
|
|
1093
1115
|
}
|
|
1094
1116
|
|
|
1095
|
-
/**
|
|
1096
|
-
* Pi's provider authentication as a sandbox `ProviderAuthSource`. CI writes
|
|
1097
|
-
* `auth.json` under `PI_CODING_AGENT_DIR`; local runs fall back to the
|
|
1098
|
-
* canonical `~/.pi/agent` dir when the override is unset.
|
|
1099
|
-
*/
|
|
1100
|
-
export declare function piProviderAuth(): ProviderAuthSource;
|
|
1101
|
-
|
|
1102
1117
|
export declare type PiRetryTriage = (input: PiRetryTriageInput) => Promise<PiRetryTriageResult>;
|
|
1103
1118
|
|
|
1104
1119
|
export declare type PiRetryTriageConfidence = RetryTriageConfidence;
|
|
@@ -1249,8 +1264,6 @@ declare interface PiWorkspaceSeedPlan {
|
|
|
1249
1264
|
|
|
1250
1265
|
export { prepareBrokeredHttpSecrets }
|
|
1251
1266
|
|
|
1252
|
-
export { ProviderAuthSource }
|
|
1253
|
-
|
|
1254
1267
|
export declare interface ProviderErrorRetryEvent extends Record<string, unknown> {
|
|
1255
1268
|
event: 'provider_error_retry';
|
|
1256
1269
|
retry: number;
|
|
@@ -1283,7 +1296,7 @@ export declare interface ResolvedGondolinTemplate {
|
|
|
1283
1296
|
resumeCommands: readonly ResumeCommand[];
|
|
1284
1297
|
}
|
|
1285
1298
|
|
|
1286
|
-
export declare function resolveHostExecBaseEnv(
|
|
1299
|
+
export declare function resolveHostExecBaseEnv(agentEnv: Readonly<Record<string, string | undefined>>): Set<string>;
|
|
1287
1300
|
|
|
1288
1301
|
/**
|
|
1289
1302
|
* Resolve the session's tool policy at start-up.
|
|
@@ -1329,8 +1342,10 @@ export declare function resolveTaskWorktreePath(mainRepo: string, workspaceId: s
|
|
|
1329
1342
|
export { ResumeCommand }
|
|
1330
1343
|
|
|
1331
1344
|
/**
|
|
1332
|
-
* Resume a Gondolin VM for a Pi session.
|
|
1333
|
-
*
|
|
1345
|
+
* Resume a Gondolin VM for a Pi session. The Pi coding-agent session and its
|
|
1346
|
+
* model calls run host-side (`createAgentSession` reads the host `~/.pi/agent`
|
|
1347
|
+
* auth), so the guest carries no provider auth — it only executes Gondolin
|
|
1348
|
+
* tools via `vm.exec`. This is a thin pass-through to the sandbox package.
|
|
1334
1349
|
*/
|
|
1335
1350
|
export declare function resumeVm(config: VmConfig): Promise<ManagedVm>;
|
|
1336
1351
|
|
|
@@ -1369,12 +1384,6 @@ declare interface RuntimeInstructorSandbox {
|
|
|
1369
1384
|
origin: string;
|
|
1370
1385
|
operations: readonly string[];
|
|
1371
1386
|
}[];
|
|
1372
|
-
/**
|
|
1373
|
-
* Guest credential mode. In `host-authenticated` the guest CLI carries no
|
|
1374
|
-
* identity credentials, so REST-backed CLI paths (e.g. `moltnet entry
|
|
1375
|
-
* create-signed`) cannot authenticate; host-side SDK tools must be used.
|
|
1376
|
-
*/
|
|
1377
|
-
guestCredentialMode?: 'guest-config' | 'host-authenticated';
|
|
1378
1387
|
}
|
|
1379
1388
|
|
|
1380
1389
|
declare interface RuntimeInstructorToolPolicy {
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash as createHash$1 } from "crypto";
|
|
|
4
4
|
import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID, buildTaskUserPrompt, createHostCapabilityRouter, defineHostCapability, getSubmitOutputContract, materializeTaskOutput, mergeRuntimeProfileContext, resolveTaskContext, taskTypeUsesSubagents, traceRuntimePhase, validateTaskOutput, validateTaskSubmission } from "@themoltnet/agent-runtime";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
|
-
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync,
|
|
7
|
+
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from "node:fs";
|
|
8
8
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
9
9
|
import path, { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
10
|
import { pipeline } from "node:stream/promises";
|
|
@@ -15,7 +15,7 @@ import { base32 } from "multiformats/bases/base32";
|
|
|
15
15
|
import { CID } from "multiformats/cid";
|
|
16
16
|
import * as raw from "multiformats/codecs/raw";
|
|
17
17
|
import { create } from "multiformats/hashes/digest";
|
|
18
|
-
import { BrokeredHttpSecretBoundaryError, GONDOLIN_BASE_EXECUTABLES, GUEST_TASK_CONTEXT_MOUNT, GuestEnvironmentBoundaryError, activateAgentEnv, activateAgentEnv as activateAgentEnv$1, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, canonicalizeBrokeredHttpSecretDescriptor, ensureSnapshot, ensureSnapshot as ensureSnapshot$1, findMainWorktree, findMainWorktree as findMainWorktree$1, isResolvedPathInsideRoot, isResolvedPathInsideRoot as isResolvedPathInsideRoot$1, loadCredentials, prepareBrokeredHttpSecrets, resolveVfsShadowConfig, resumeVm as resumeVm$1 } from "@themoltnet/sandbox-gondolin";
|
|
18
|
+
import { BrokeredHttpSecretBoundaryError, GONDOLIN_BASE_EXECUTABLES, GUEST_TASK_CONTEXT_MOUNT, GuestEnvironmentBoundaryError, activateAgentEnv, activateAgentEnv as activateAgentEnv$1, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, canonicalizeBrokeredHttpSecretDescriptor, ensureSnapshot, ensureSnapshot as ensureSnapshot$1, execManagedCommand, findMainWorktree, findMainWorktree as findMainWorktree$1, isResolvedPathInsideRoot, isResolvedPathInsideRoot as isResolvedPathInsideRoot$1, loadCredentials, prepareBrokeredHttpSecrets, resolveVfsShadowConfig, resumeVm as resumeVm$1 } from "@themoltnet/sandbox-gondolin";
|
|
19
19
|
import { SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
20
20
|
import * as json from "multiformats/codecs/json";
|
|
21
21
|
import { sha256 as sha256$1 } from "multiformats/hashes/sha2";
|
|
@@ -3458,6 +3458,37 @@ function assertRuntimeKind(value) {
|
|
|
3458
3458
|
*/
|
|
3459
3459
|
var DEFAULT_GREP_LIMIT = 100;
|
|
3460
3460
|
var GREP_MAX_FILE_SIZE = "2M";
|
|
3461
|
+
var GondolinVmRetiredError = class extends Error {
|
|
3462
|
+
code = "sandbox_retired";
|
|
3463
|
+
constructor(retirement, options) {
|
|
3464
|
+
super(`Sandbox VM retired after ${retirement.trigger}: ${retirement.reason}`, options);
|
|
3465
|
+
this.retirement = retirement;
|
|
3466
|
+
this.name = "GondolinVmRetiredError";
|
|
3467
|
+
}
|
|
3468
|
+
};
|
|
3469
|
+
function createGondolinToolLifecycle(config = {}) {
|
|
3470
|
+
let retirement = null;
|
|
3471
|
+
return {
|
|
3472
|
+
assertActive() {
|
|
3473
|
+
if (retirement) throw new GondolinVmRetiredError(retirement);
|
|
3474
|
+
},
|
|
3475
|
+
getRetirement: () => retirement,
|
|
3476
|
+
markRetired(next) {
|
|
3477
|
+
if (retirement) return;
|
|
3478
|
+
retirement = next;
|
|
3479
|
+
config.onRetired?.(next);
|
|
3480
|
+
}
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
function guardGondolinToolDefinitions(tools, lifecycle) {
|
|
3484
|
+
return tools.map((tool) => ({
|
|
3485
|
+
...tool,
|
|
3486
|
+
execute(...args) {
|
|
3487
|
+
lifecycle.assertActive();
|
|
3488
|
+
return tool.execute(...args);
|
|
3489
|
+
}
|
|
3490
|
+
}));
|
|
3491
|
+
}
|
|
3461
3492
|
function shQuote(s) {
|
|
3462
3493
|
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
3463
3494
|
}
|
|
@@ -3806,38 +3837,45 @@ async function executeGondolinGrep(vm, localCwd, guestWorkspace, params, signal)
|
|
|
3806
3837
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
3807
3838
|
};
|
|
3808
3839
|
}
|
|
3809
|
-
function createGondolinBashOps(vm, localCwd, guestWorkspace) {
|
|
3840
|
+
function createGondolinBashOps(vm, localCwd, guestWorkspace, config) {
|
|
3810
3841
|
return { exec: async (command, cwd, { onData, signal, timeout, env }) => {
|
|
3811
|
-
|
|
3812
|
-
const
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
"
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3842
|
+
config.lifecycle.assertActive();
|
|
3843
|
+
const result = await execManagedCommand(vm, command, {
|
|
3844
|
+
cwd: toGuestPath(localCwd, cwd, guestWorkspace),
|
|
3845
|
+
signal,
|
|
3846
|
+
timeoutMs: timeout && timeout > 0 ? timeout * 1e3 : void 0,
|
|
3847
|
+
onData
|
|
3848
|
+
});
|
|
3849
|
+
if (result.termination.status === "backend-retired") {
|
|
3850
|
+
const retirement = {
|
|
3851
|
+
backendRetired: true,
|
|
3852
|
+
reason: "backend-retired",
|
|
3853
|
+
trigger: result.timedOut ? "timeout" : "cancellation"
|
|
3854
|
+
};
|
|
3855
|
+
config.lifecycle.markRetired(retirement);
|
|
3856
|
+
try {
|
|
3857
|
+
await config.retireVm(retirement);
|
|
3858
|
+
} catch (error) {
|
|
3859
|
+
throw new GondolinVmRetiredError(retirement, { cause: error });
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
if (result.termination.status === "recovery-required") {
|
|
3863
|
+
const retirement = {
|
|
3864
|
+
backendRetired: false,
|
|
3865
|
+
reason: result.termination.reason,
|
|
3866
|
+
trigger: result.timedOut ? "timeout" : "cancellation"
|
|
3867
|
+
};
|
|
3868
|
+
config.lifecycle.markRetired(retirement);
|
|
3869
|
+
try {
|
|
3870
|
+
await config.retireVm(retirement);
|
|
3871
|
+
} catch (error) {
|
|
3872
|
+
throw new GondolinVmRetiredError(retirement, { cause: error });
|
|
3873
|
+
}
|
|
3874
|
+
throw new GondolinVmRetiredError(retirement);
|
|
3840
3875
|
}
|
|
3876
|
+
if (result.timedOut) throw new Error(`timeout:${timeout}`);
|
|
3877
|
+
if (result.cancelled) throw new Error("aborted");
|
|
3878
|
+
return { exitCode: result.exitCode };
|
|
3841
3879
|
} };
|
|
3842
3880
|
}
|
|
3843
3881
|
//#endregion
|
|
@@ -4151,31 +4189,14 @@ function decisionContext(deps) {
|
|
|
4151
4189
|
}
|
|
4152
4190
|
//#endregion
|
|
4153
4191
|
//#region src/vm.ts
|
|
4154
|
-
/** Guest path where Pi expects its auth blob. */
|
|
4155
|
-
var PI_GUEST_AUTH_PATH = "/home/agent/.pi/agent/auth.json";
|
|
4156
4192
|
/**
|
|
4157
|
-
*
|
|
4158
|
-
*
|
|
4159
|
-
*
|
|
4160
|
-
|
|
4161
|
-
function piProviderAuth() {
|
|
4162
|
-
return {
|
|
4163
|
-
guestPath: PI_GUEST_AUTH_PATH,
|
|
4164
|
-
load: () => {
|
|
4165
|
-
const authPath = path.join(resolvePiCodingAgentDir(), "auth.json");
|
|
4166
|
-
return existsSync(authPath) ? readFileSync(authPath, "utf8") : null;
|
|
4167
|
-
}
|
|
4168
|
-
};
|
|
4169
|
-
}
|
|
4170
|
-
/**
|
|
4171
|
-
* Resume a Gondolin VM for a Pi session. Identical to the sandbox package's
|
|
4172
|
-
* `resumeVm`, with Pi's provider auth supplied unless the caller overrides it.
|
|
4193
|
+
* Resume a Gondolin VM for a Pi session. The Pi coding-agent session and its
|
|
4194
|
+
* model calls run host-side (`createAgentSession` reads the host `~/.pi/agent`
|
|
4195
|
+
* auth), so the guest carries no provider auth — it only executes Gondolin
|
|
4196
|
+
* tools via `vm.exec`. This is a thin pass-through to the sandbox package.
|
|
4173
4197
|
*/
|
|
4174
4198
|
function resumeVm(config) {
|
|
4175
|
-
return resumeVm$1(
|
|
4176
|
-
providerAuth: piProviderAuth(),
|
|
4177
|
-
...config
|
|
4178
|
-
});
|
|
4199
|
+
return resumeVm$1(config);
|
|
4179
4200
|
}
|
|
4180
4201
|
//#endregion
|
|
4181
4202
|
//#region src/runtime/capability-discovery.ts
|
|
@@ -4702,20 +4723,9 @@ function buildCredentialInstructions(policy, sandbox) {
|
|
|
4702
4723
|
}
|
|
4703
4724
|
if (gitAvailable) {
|
|
4704
4725
|
const signing = (sandbox?.hostCapabilities ?? []).find((capability) => capability.name === "agent-signing");
|
|
4705
|
-
|
|
4706
|
-
const hostAuthenticated = sandbox?.guestCredentialMode === "host-authenticated";
|
|
4707
|
-
if (signingGranted) {
|
|
4726
|
+
if (!!signing && (!policy || policy.enforcement === "off" || policy.allowedTools.some((name) => name === "capability:agent-signing" || name.startsWith("capability:agent-signing:")))) {
|
|
4708
4727
|
lines.push("- Commit signing is brokered: `git commit -S` works normally through", " `SSH_AUTH_SOCK`; the signing key stays on the host. Do not look for,", " export, or recreate a private key in the guest.");
|
|
4709
|
-
lines.push(
|
|
4710
|
-
"- For a signed diary entry use the `moltnet_create_entry` tool with",
|
|
4711
|
-
" `signed: true` (it signs on the trusted host). Do not use the",
|
|
4712
|
-
" guest `moltnet entry create-signed` CLI: it has no guest identity",
|
|
4713
|
-
" credentials and its REST calls will fail."
|
|
4714
|
-
] : [
|
|
4715
|
-
"- For a signed diary entry use `moltnet_create_entry` with",
|
|
4716
|
-
" `signed: true`, or the guest `moltnet entry create-signed` CLI",
|
|
4717
|
-
" (brokered through `MOLTNET_SIGNER_URL`)."
|
|
4718
|
-
]);
|
|
4728
|
+
lines.push("- For a signed diary entry use the `moltnet_create_entry` tool with", " `signed: true` (it signs on the trusted host). Do not use the", " guest `moltnet entry create-signed` CLI: it has no guest identity", " credentials and its REST calls will fail.");
|
|
4719
4729
|
} else if (signing) lines.push("- An `agent-signing` capability is declared but the active policy does", " not grant it. `git commit -S` and signed diary entries will be denied;", " create unsigned commits/entries unless the capability is granted.");
|
|
4720
4730
|
else lines.push("- Local Git commands run inside the guest. No signing key or Git", " credential helper is injected; signing and authenticated push", " require an explicitly provided capability.");
|
|
4721
4731
|
}
|
|
@@ -5781,12 +5791,10 @@ function createPolicyCheckedSigner(router, identity) {
|
|
|
5781
5791
|
}
|
|
5782
5792
|
};
|
|
5783
5793
|
}
|
|
5784
|
-
function resolveHostExecBaseEnv(
|
|
5794
|
+
function resolveHostExecBaseEnv(agentEnv) {
|
|
5785
5795
|
const names = new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(agentEnv)]);
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
for (const name of names) if (name.startsWith("MOLTNET_")) names.delete(name);
|
|
5789
|
-
}
|
|
5796
|
+
for (const name of HOST_AUTHENTICATED_HOST_EXEC_REFUSED_ENV) names.delete(name);
|
|
5797
|
+
for (const name of names) if (name.startsWith("MOLTNET_")) names.delete(name);
|
|
5790
5798
|
return names;
|
|
5791
5799
|
}
|
|
5792
5800
|
var noopTurnEventHandler = () => {};
|
|
@@ -5802,13 +5810,16 @@ async function openVmWorkspaceFileForRead(config) {
|
|
|
5802
5810
|
};
|
|
5803
5811
|
}
|
|
5804
5812
|
function createGondolinToolDefinitions(config) {
|
|
5805
|
-
const { vm, cwdPath, guestWorkspace } = config;
|
|
5813
|
+
const { vm, cwdPath, guestWorkspace, lifecycle, retireVm } = config;
|
|
5806
5814
|
const grepTool = createGrepToolDefinition(cwdPath);
|
|
5807
|
-
return [
|
|
5815
|
+
return guardGondolinToolDefinitions([
|
|
5808
5816
|
createReadToolDefinition(cwdPath, { operations: createGondolinReadOps(vm, cwdPath, guestWorkspace) }),
|
|
5809
5817
|
createWriteToolDefinition(cwdPath, { operations: createGondolinWriteOps(vm, cwdPath, guestWorkspace) }),
|
|
5810
5818
|
createEditToolDefinition(cwdPath, { operations: createGondolinEditOps(vm, cwdPath, guestWorkspace) }),
|
|
5811
|
-
createBashToolDefinition(cwdPath, { operations: createGondolinBashOps(vm, cwdPath, guestWorkspace
|
|
5819
|
+
createBashToolDefinition(cwdPath, { operations: createGondolinBashOps(vm, cwdPath, guestWorkspace, {
|
|
5820
|
+
lifecycle,
|
|
5821
|
+
retireVm
|
|
5822
|
+
}) }),
|
|
5812
5823
|
createLsToolDefinition(cwdPath, { operations: createGondolinLsOps(vm, cwdPath, guestWorkspace) }),
|
|
5813
5824
|
createFindToolDefinition(cwdPath, { operations: createGondolinFindOps(vm, cwdPath, guestWorkspace) }),
|
|
5814
5825
|
{
|
|
@@ -5818,7 +5829,52 @@ function createGondolinToolDefinitions(config) {
|
|
|
5818
5829
|
return executeGondolinGrep(vm, cwdPath, guestWorkspace, params, signal);
|
|
5819
5830
|
}
|
|
5820
5831
|
}
|
|
5821
|
-
];
|
|
5832
|
+
], lifecycle);
|
|
5833
|
+
}
|
|
5834
|
+
async function retireManagedGondolinVm(config) {
|
|
5835
|
+
const { managed, retirement, secretEnvNames, release } = config;
|
|
5836
|
+
const recoveryErrors = [];
|
|
5837
|
+
for (const guestEnv of secretEnvNames) try {
|
|
5838
|
+
managed.secretManager.revokeSecret(guestEnv);
|
|
5839
|
+
} catch (error) {
|
|
5840
|
+
recoveryErrors.push(error);
|
|
5841
|
+
}
|
|
5842
|
+
try {
|
|
5843
|
+
if (!retirement.backendRetired) await managed.vm.close();
|
|
5844
|
+
} catch (error) {
|
|
5845
|
+
recoveryErrors.push(error);
|
|
5846
|
+
} finally {
|
|
5847
|
+
release();
|
|
5848
|
+
}
|
|
5849
|
+
if (recoveryErrors.length > 0) throw new AggregateError(recoveryErrors, "Sandbox VM retirement recovery failed");
|
|
5850
|
+
}
|
|
5851
|
+
function createGondolinRetirementCoordinator(config = {}) {
|
|
5852
|
+
let retirement = null;
|
|
5853
|
+
let abortHandler = null;
|
|
5854
|
+
return {
|
|
5855
|
+
lifecycle: createGondolinToolLifecycle({ onRetired(next) {
|
|
5856
|
+
retirement = next;
|
|
5857
|
+
config.onRetired?.(next);
|
|
5858
|
+
abortHandler?.(next);
|
|
5859
|
+
} }),
|
|
5860
|
+
bindAbortHandler(handler) {
|
|
5861
|
+
abortHandler = handler;
|
|
5862
|
+
if (retirement) handler(retirement);
|
|
5863
|
+
},
|
|
5864
|
+
getRetirement: () => retirement
|
|
5865
|
+
};
|
|
5866
|
+
}
|
|
5867
|
+
function guardGondolinExtensionFactories(factories, lifecycle) {
|
|
5868
|
+
return factories.map((factory) => (pi) => {
|
|
5869
|
+
factory(new Proxy(pi, { get(target, property, receiver) {
|
|
5870
|
+
if (property === "registerTool") return (tool) => {
|
|
5871
|
+
const [guardedTool] = guardGondolinToolDefinitions([tool], lifecycle);
|
|
5872
|
+
if (guardedTool) target.registerTool(guardedTool);
|
|
5873
|
+
};
|
|
5874
|
+
const value = Reflect.get(target, property, receiver);
|
|
5875
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
5876
|
+
} }));
|
|
5877
|
+
});
|
|
5822
5878
|
}
|
|
5823
5879
|
/** Resolve one attempt's host-only HTTP credentials before VM resume. */
|
|
5824
5880
|
async function resolveAttemptBrokeredHttpSecrets(input) {
|
|
@@ -6079,7 +6135,6 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6079
6135
|
checkpointPath,
|
|
6080
6136
|
agentName: opts.agentName,
|
|
6081
6137
|
agentRootDir,
|
|
6082
|
-
guestCredentialMode: opts.guestCredentialMode,
|
|
6083
6138
|
mountPath,
|
|
6084
6139
|
workspaceMode: preparedWorkspace.mode,
|
|
6085
6140
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
@@ -6107,6 +6162,23 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6107
6162
|
activateAgentEnv$1(managed.credentials.agentEnv, agentRootDir);
|
|
6108
6163
|
const activeWorkspace = preparedWorkspace;
|
|
6109
6164
|
const activeManaged = managed;
|
|
6165
|
+
const sandboxRetirementEvents = [];
|
|
6166
|
+
const sandboxRetirementCoordinator = createGondolinRetirementCoordinator({ onRetired(retirement) {
|
|
6167
|
+
const details = {
|
|
6168
|
+
event: "sandbox_vm_retired",
|
|
6169
|
+
taskId: task.id,
|
|
6170
|
+
attemptN,
|
|
6171
|
+
vmId: activeManaged.vm.id,
|
|
6172
|
+
outcome: retirement.backendRetired ? "retired" : "retirement_failed",
|
|
6173
|
+
...retirement
|
|
6174
|
+
};
|
|
6175
|
+
process.stderr.write(`${JSON.stringify(details)}\n`);
|
|
6176
|
+
sandboxRetirementEvents.push(emitError("sandbox_retirement", `Sandbox VM retired after ${retirement.trigger}`, details).catch((error) => {
|
|
6177
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6178
|
+
process.stderr.write(`[sandbox] failed to emit retirement event: ${message}\n`);
|
|
6179
|
+
}));
|
|
6180
|
+
} });
|
|
6181
|
+
const gondolinLifecycle = sandboxRetirementCoordinator.lifecycle;
|
|
6110
6182
|
const getMoltNetAgent = createMoltNetAgentResolver({
|
|
6111
6183
|
moltnetAgent: opts.moltnetAgent,
|
|
6112
6184
|
configDir: managed.agentDir
|
|
@@ -6222,7 +6294,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6222
6294
|
const gondolinCustomTools = createGondolinToolDefinitions({
|
|
6223
6295
|
vm: managed.vm,
|
|
6224
6296
|
cwdPath,
|
|
6225
|
-
guestWorkspace: managed.guestWorkspace
|
|
6297
|
+
guestWorkspace: managed.guestWorkspace,
|
|
6298
|
+
lifecycle: gondolinLifecycle,
|
|
6299
|
+
retireVm: async (retirement) => {
|
|
6300
|
+
const recoveringManaged = managed;
|
|
6301
|
+
if (!recoveringManaged) return;
|
|
6302
|
+
await retireManagedGondolinVm({
|
|
6303
|
+
managed: recoveringManaged,
|
|
6304
|
+
retirement,
|
|
6305
|
+
secretEnvNames: brokeredSecretEnvNames,
|
|
6306
|
+
release: () => {
|
|
6307
|
+
if (managed === recoveringManaged) managed = null;
|
|
6308
|
+
}
|
|
6309
|
+
});
|
|
6310
|
+
}
|
|
6226
6311
|
});
|
|
6227
6312
|
const submitCompletion = createSubmitCompletionCoordinator({
|
|
6228
6313
|
onDrained: () => session?.abort(),
|
|
@@ -6249,13 +6334,16 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6249
6334
|
getSessionErrors: () => [],
|
|
6250
6335
|
clearSessionErrors: () => {},
|
|
6251
6336
|
getHostCwd: () => cwdPath,
|
|
6252
|
-
openWorkspaceFileForRead: (filePath) =>
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6337
|
+
openWorkspaceFileForRead: (filePath) => {
|
|
6338
|
+
gondolinLifecycle.assertActive();
|
|
6339
|
+
return openVmWorkspaceFileForRead({
|
|
6340
|
+
vm: activeManaged.vm,
|
|
6341
|
+
cwdPath,
|
|
6342
|
+
guestWorkspace: activeManaged.guestWorkspace,
|
|
6343
|
+
filePath
|
|
6344
|
+
});
|
|
6345
|
+
},
|
|
6346
|
+
hostExecBaseEnv: resolveHostExecBaseEnv(managed.credentials.agentEnv),
|
|
6259
6347
|
hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
|
|
6260
6348
|
getTaskContext: () => ({
|
|
6261
6349
|
taskId: task.id,
|
|
@@ -6334,30 +6422,30 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6334
6422
|
cwdPath,
|
|
6335
6423
|
guestWorkspace: managed.guestWorkspace
|
|
6336
6424
|
};
|
|
6337
|
-
const runtimeParentTools = opts.runtimeDefinition ? await materializePiTools({
|
|
6425
|
+
const runtimeParentTools = opts.runtimeDefinition ? guardGondolinToolDefinitions(await materializePiTools({
|
|
6338
6426
|
runtime: opts.runtimeDefinition,
|
|
6339
6427
|
context: runtimeToolContext,
|
|
6340
6428
|
target: "parent",
|
|
6341
6429
|
policy: resolvedToolPolicy
|
|
6342
|
-
}) : [];
|
|
6343
|
-
const runtimeSubagentTools = opts.runtimeDefinition ? await materializePiTools({
|
|
6430
|
+
}), gondolinLifecycle) : [];
|
|
6431
|
+
const runtimeSubagentTools = opts.runtimeDefinition ? guardGondolinToolDefinitions(await materializePiTools({
|
|
6344
6432
|
runtime: opts.runtimeDefinition,
|
|
6345
6433
|
context: runtimeToolContext,
|
|
6346
6434
|
target: "subagent",
|
|
6347
6435
|
policy: resolvedToolPolicy
|
|
6348
|
-
}) : [];
|
|
6349
|
-
const runtimeParentExtensions = opts.runtimeDefinition ? await materializePiExtensions({
|
|
6436
|
+
}), gondolinLifecycle) : [];
|
|
6437
|
+
const runtimeParentExtensions = opts.runtimeDefinition ? guardGondolinExtensionFactories(await materializePiExtensions({
|
|
6350
6438
|
runtime: opts.runtimeDefinition,
|
|
6351
6439
|
context: runtimeToolContext,
|
|
6352
6440
|
target: "parent",
|
|
6353
6441
|
policy: resolvedToolPolicy
|
|
6354
|
-
}) : [];
|
|
6355
|
-
const runtimeSubagentExtensions = opts.runtimeDefinition ? await materializePiExtensions({
|
|
6442
|
+
}), gondolinLifecycle) : [];
|
|
6443
|
+
const runtimeSubagentExtensions = opts.runtimeDefinition ? guardGondolinExtensionFactories(await materializePiExtensions({
|
|
6356
6444
|
runtime: opts.runtimeDefinition,
|
|
6357
6445
|
context: runtimeToolContext,
|
|
6358
6446
|
target: "subagent",
|
|
6359
6447
|
policy: resolvedToolPolicy
|
|
6360
|
-
}) : [];
|
|
6448
|
+
}), gondolinLifecycle) : [];
|
|
6361
6449
|
const visibleBaseTools = filterModelVisibleTools([...gondolinCustomTools, ...moltnetTools], resolvedToolPolicy);
|
|
6362
6450
|
const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
|
|
6363
6451
|
const visibleParentToolNames = modelVisiblePiToolNames({
|
|
@@ -6410,7 +6498,6 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6410
6498
|
allowedHosts: [...effectiveSandboxConfig?.network?.allowedHosts ?? [], ...opts.extraAllowedHosts ?? []],
|
|
6411
6499
|
allowedInternalHosts: effectiveSandboxConfig?.network?.allowedInternalHosts ?? [],
|
|
6412
6500
|
brokeredSecretEnvNames,
|
|
6413
|
-
guestCredentialMode: opts.guestCredentialMode ?? "guest-config",
|
|
6414
6501
|
...capabilityRouter && { hostCapabilities: capabilityRouter.manifest }
|
|
6415
6502
|
},
|
|
6416
6503
|
toolPolicy: capabilityProjection.instructorPolicy
|
|
@@ -6449,12 +6536,12 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6449
6536
|
});
|
|
6450
6537
|
parentSubagentTools.push(subagentHandle.tool);
|
|
6451
6538
|
}
|
|
6452
|
-
const parentTools = [
|
|
6539
|
+
const parentTools = guardGondolinToolDefinitions([
|
|
6453
6540
|
...visibleBaseTools,
|
|
6454
6541
|
...runtimeParentTools,
|
|
6455
6542
|
...submitTools,
|
|
6456
6543
|
...parentSubagentTools
|
|
6457
|
-
];
|
|
6544
|
+
], gondolinLifecycle);
|
|
6458
6545
|
session = await traceRuntimePhase("moltnet.execution.session.create", {
|
|
6459
6546
|
"gen_ai.provider.name": opts.provider,
|
|
6460
6547
|
"gen_ai.request.model": opts.model
|
|
@@ -6547,6 +6634,9 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6547
6634
|
message
|
|
6548
6635
|
}));
|
|
6549
6636
|
};
|
|
6637
|
+
sandboxRetirementCoordinator.bindAbortHandler((retirement) => {
|
|
6638
|
+
triggerCapAbort("sandbox_retired", `Sandbox VM retired after ${retirement.trigger}: ${retirement.reason}.`);
|
|
6639
|
+
});
|
|
6550
6640
|
session.subscribe(makeSessionEventHandler({
|
|
6551
6641
|
state: turnState,
|
|
6552
6642
|
usage,
|
|
@@ -6616,7 +6706,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
6616
6706
|
event: "subagent_summary",
|
|
6617
6707
|
callCount: subagentHandle.getCallCount()
|
|
6618
6708
|
});
|
|
6619
|
-
await Promise.all(recordingPromise);
|
|
6709
|
+
await Promise.all([...recordingPromise, ...sandboxRetirementEvents]);
|
|
6620
6710
|
const cancelled = reporter.cancelSignal.aborted;
|
|
6621
6711
|
let parsedOutput = null;
|
|
6622
6712
|
let parsedOutputCid = null;
|
|
@@ -7387,4 +7477,4 @@ function describeToolErrorMessage(result) {
|
|
|
7387
7477
|
}
|
|
7388
7478
|
}
|
|
7389
7479
|
//#endregion
|
|
7390
|
-
export { BrokeredHttpSecretBoundaryError, DEFAULT_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS, GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, GUEST_ALLOWED_SIGNERS_PATH, GUEST_GITCONFIG_PATH, GUEST_SIGNER_SOCKET, GuestEnvironmentBoundaryError, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION,
|
|
7480
|
+
export { BrokeredHttpSecretBoundaryError, DEFAULT_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS, GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, GUEST_ALLOWED_SIGNERS_PATH, GUEST_GITCONFIG_PATH, GUEST_SIGNER_SOCKET, GondolinVmRetiredError, GuestEnvironmentBoundaryError, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION, PI_RUNTIME_DEFINITION_VERSION, PiBrokeredHttpSecretResolutionError, activateAgentEnv, agentSigningCapability, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, buildAgentSession, buildPiExecutorManifest, buildRuntimeKernel, buildWorkspaceMountInstructions, createGondolinBashOps, createGondolinEditOps, createGondolinFindOps, createGondolinLsOps, createGondolinReadOps, createGondolinToolDefinitions, createGondolinToolLifecycle, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, createToolPolicyExtension, decideForEvent, decideToolCall, defineGondolinTemplate, definePiBrokeredHttpSecret, definePiExtension, definePiRuntime, definePiTool, enabledPiToolNames, ensureSnapshot, executeGondolinGrep, executePiTask, filterModelVisibleTools, findMainWorktree, guardGondolinToolDefinitions, injectRuntimeContext as injectTaskContext, isKernelTool, isResolvedPathInsideRoot, isToolVisible, loadCredentials, materializePiBrokeredHttpSecrets, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, prepareBrokeredHttpSecrets, 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.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Composable MoltNet runtime kernel for Pi agents in Gondolin VMs",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"!dist/.dts"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@earendil-works/gondolin": "^0.
|
|
26
|
+
"@earendil-works/gondolin": "^0.12.0",
|
|
27
27
|
"@ipld/dag-cbor": "^9.2.5",
|
|
28
28
|
"@noble/curves": "^2.0.0",
|
|
29
29
|
"@noble/ed25519": "^2.0.0",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"multiformats": "^13.3.0",
|
|
33
33
|
"typebox": "^1.2.8",
|
|
34
34
|
"@themoltnet/agent-runtime": "0.44.0",
|
|
35
|
-
"@themoltnet/sandbox-gondolin": "0.3.0",
|
|
36
|
-
"@themoltnet/sdk": "0.137.0",
|
|
37
35
|
"@themoltnet/os-keyring": "0.2.0",
|
|
38
|
-
"@themoltnet/
|
|
36
|
+
"@themoltnet/sdk": "0.137.0",
|
|
37
|
+
"@themoltnet/shell-command-analyzer": "0.3.0",
|
|
38
|
+
"@themoltnet/sandbox-gondolin": "0.4.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-ai": "0.79.4",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"vite": "^8.0.0",
|
|
53
53
|
"vite-plugin-dts": "^4.5.4",
|
|
54
54
|
"vitest": "^3.0.0",
|
|
55
|
-
"@moltnet/
|
|
56
|
-
"@moltnet/tasks": "0.1.0",
|
|
55
|
+
"@moltnet/runtime-profiles": "0.1.0",
|
|
57
56
|
"@moltnet/models": "0.1.0",
|
|
58
|
-
"@moltnet/
|
|
57
|
+
"@moltnet/crypto-service": "0.1.0",
|
|
58
|
+
"@moltnet/tasks": "0.1.0"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=24"
|