@themoltnet/pi-extension 0.37.1 → 0.38.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.js +97 -18
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
4
|
-
import path, { join } from "node:path";
|
|
4
|
+
import path, { isAbsolute, join, relative } from "node:path";
|
|
5
5
|
import { DEFAULT_MAX_BYTES, createBashTool, createEditTool, createFindTool, createGrepTool, createLsTool, createReadTool, createWriteTool, defineTool, formatSize, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { readFile, realpath, stat } from "node:fs/promises";
|
|
6
|
+
import { mkdir, readFile, realpath, stat } from "node:fs/promises";
|
|
7
7
|
import { pipeline } from "node:stream/promises";
|
|
8
8
|
import { Type } from "@earendil-works/pi-ai";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
9
|
import crypto, { createHash } from "crypto";
|
|
11
10
|
import { createHash as createHash$1 } from "node:crypto";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
12
|
import { Readable } from "node:stream";
|
|
13
13
|
import { MemoryProvider, RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, createShadowPathPredicate, ensureImageSelector, isWriteFlag, loadGuestAssets } from "@earendil-works/gondolin";
|
|
14
14
|
import { parseEnv } from "node:util";
|
|
@@ -41,6 +41,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
41
41
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp$1({}, "__esModule", { value: true }), mod);
|
|
42
42
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
43
43
|
//#endregion
|
|
44
|
+
//#region ../pi-runtime/src/path-containment.ts
|
|
45
|
+
/**
|
|
46
|
+
* Check containment for already-resolved lexical or real paths.
|
|
47
|
+
*
|
|
48
|
+
* Callers that accept untrusted paths must resolve/realpath at their I/O
|
|
49
|
+
* boundary first; keeping the platform-specific relative-path rule here avoids
|
|
50
|
+
* subtly different `..` and absolute-path handling across runtime cleanup,
|
|
51
|
+
* session sync, and artifact staging.
|
|
52
|
+
*/
|
|
53
|
+
function isResolvedPathInsideRoot(path, root) {
|
|
54
|
+
const rel = relative(root, path);
|
|
55
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
44
58
|
//#region ../pi-runtime/src/moltnet/render-phase6.ts
|
|
45
59
|
function slugToTitle(value) {
|
|
46
60
|
return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -232,11 +246,32 @@ async function openWorkspaceArtifactInput(config, cwd, filePath) {
|
|
|
232
246
|
}
|
|
233
247
|
async function resolveWorkspaceOutputPath(cwd, filePath) {
|
|
234
248
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
if (
|
|
249
|
+
const workspaceRoot = path.resolve(cwd);
|
|
250
|
+
const lexicalRel = path.relative(workspaceRoot, resolved);
|
|
251
|
+
if (lexicalRel === "" || lexicalRel.startsWith("..") || path.isAbsolute(lexicalRel)) throw new Error(`task artifact output path escapes workspace: ${filePath}`);
|
|
252
|
+
const realCwd = await realpath(cwd);
|
|
253
|
+
const parent = path.dirname(resolved);
|
|
254
|
+
assertPathInsideWorkspace(realCwd, await findExistingAncestor(parent), filePath);
|
|
255
|
+
await mkdir(parent, { recursive: true });
|
|
256
|
+
assertPathInsideWorkspace(realCwd, await realpath(parent), filePath);
|
|
238
257
|
return resolved;
|
|
239
258
|
}
|
|
259
|
+
async function findExistingAncestor(candidate) {
|
|
260
|
+
let current = candidate;
|
|
261
|
+
for (;;) {
|
|
262
|
+
try {
|
|
263
|
+
return await realpath(current);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (!err || typeof err !== "object" || !("code" in err) || err.code !== "ENOENT") throw err;
|
|
266
|
+
}
|
|
267
|
+
const parent = path.dirname(current);
|
|
268
|
+
if (parent === current) throw new Error(`task artifact output has no existing ancestor: ${candidate}`);
|
|
269
|
+
current = parent;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function assertPathInsideWorkspace(realCwd, realPath, displayPath) {
|
|
273
|
+
if (!isResolvedPathInsideRoot(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
|
|
274
|
+
}
|
|
240
275
|
/**
|
|
241
276
|
* Expand the `taskFilter` shorthand on the diary list/search tools into
|
|
242
277
|
* the matching `task:*` provenance tags emitted by `moltnet_create_entry`
|
|
@@ -9948,6 +9983,11 @@ var RuntimeModel = _Object_({
|
|
|
9948
9983
|
var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
9949
9984
|
version: 1,
|
|
9950
9985
|
fragments: {
|
|
9986
|
+
"artifact-planner-v1": {
|
|
9987
|
+
binding: "prompt_prefix",
|
|
9988
|
+
content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts, use shell commands, modify files, commit, branch, push, or contact GitHub.\n- Read only the exact artifact CIDs named by the task, and only when the embedded manifest does not provide enough evidence. Never paginate or discover artifacts speculatively.\n- Perform semantic classification and planning from supplied content and producer/consumer evidence. Do not substitute filename, directory, language, ecosystem, or repository-specific exclusion rules for evidence.\n- Return exactly the requested versioned structured plan through the registered submit-output tool. Do not emit a second prose or JSON representation.",
|
|
9989
|
+
slug: "artifact-planner-v1"
|
|
9990
|
+
},
|
|
9951
9991
|
"accountable-delivery-v1": {
|
|
9952
9992
|
binding: "prompt_prefix",
|
|
9953
9993
|
content: "# Accountable delivery\n\n- Pair every commit made during this task with a signed diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer.\n- Keep commit signing enabled; do not bypass the agent git configuration.\n- Push a branch and open or update a pull request only when the task asks for it. For GitHub mutations, use the credential-bound `GH_TOKEN` command form required by the runtime kernel.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
|
|
@@ -9980,6 +10020,10 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
9980
10020
|
}
|
|
9981
10021
|
},
|
|
9982
10022
|
recipes: {
|
|
10023
|
+
"artifact-planner@v1": {
|
|
10024
|
+
description: "Minimal artifact-only context for bounded semantic classification and planning.",
|
|
10025
|
+
fragments: ["artifact-planner-v1"]
|
|
10026
|
+
},
|
|
9983
10027
|
"run-eval-direct@v1": {
|
|
9984
10028
|
description: "Minimal direct context for a short, isolated evaluation run.",
|
|
9985
10029
|
fragments: ["run-eval-direct-v1"]
|
|
@@ -10617,7 +10661,11 @@ var ProblemDetailsSchema = _Object_({
|
|
|
10617
10661
|
}),
|
|
10618
10662
|
code: ProblemCodeSchema,
|
|
10619
10663
|
detail: Optional(String$1()),
|
|
10620
|
-
instance: Optional(String$1())
|
|
10664
|
+
instance: Optional(String$1()),
|
|
10665
|
+
retryAfter: Optional(Integer({
|
|
10666
|
+
minimum: 0,
|
|
10667
|
+
description: "Non-negative delay in seconds before retrying, matching the Retry-After response header when present."
|
|
10668
|
+
}))
|
|
10621
10669
|
}, {
|
|
10622
10670
|
$id: "ProblemDetails",
|
|
10623
10671
|
additionalProperties: true
|
|
@@ -11463,7 +11511,7 @@ var VerificationResult = _Object_({
|
|
|
11463
11511
|
var VerificationRecord = _Object_({
|
|
11464
11512
|
inputCid: String$1({ minLength: 1 }),
|
|
11465
11513
|
results: _Array_(VerificationResult),
|
|
11466
|
-
passed: Boolean$1()
|
|
11514
|
+
passed: Boolean$1({ description: "True iff every verification result has status \"pass\" or \"skip\"; false when any result has status \"fail\"." })
|
|
11467
11515
|
}, {
|
|
11468
11516
|
$id: "VerificationRecord",
|
|
11469
11517
|
additionalProperties: false
|
|
@@ -15157,6 +15205,16 @@ function checkVerificationInputCid(value, runtime) {
|
|
|
15157
15205
|
}];
|
|
15158
15206
|
return [];
|
|
15159
15207
|
}
|
|
15208
|
+
function checkVerificationPassedConsistency(value) {
|
|
15209
|
+
const verification = value !== null && typeof value === "object" ? value.verification : void 0;
|
|
15210
|
+
if (verification === void 0 || !Array.isArray(verification.results) || typeof verification.passed !== "boolean") return [];
|
|
15211
|
+
const expectedPassed = verification.results.every((result) => result.status !== "fail");
|
|
15212
|
+
if (verification.passed !== expectedPassed) return [{
|
|
15213
|
+
field: "output/verification/passed",
|
|
15214
|
+
message: "must be true iff no verification result has status \"fail\""
|
|
15215
|
+
}];
|
|
15216
|
+
return [];
|
|
15217
|
+
}
|
|
15160
15218
|
function validateTaskResult(taskType, value, input, runtime, submission = false) {
|
|
15161
15219
|
const entry = getTaskTypeEntry(taskType);
|
|
15162
15220
|
if (!entry) return [{
|
|
@@ -15172,7 +15230,7 @@ function validateTaskResult(taskType, value, input, runtime, submission = false)
|
|
|
15172
15230
|
message: validationError
|
|
15173
15231
|
}];
|
|
15174
15232
|
}
|
|
15175
|
-
return checkVerificationInputCid(value, runtime);
|
|
15233
|
+
return [...checkVerificationInputCid(value, runtime), ...checkVerificationPassedConsistency(value)];
|
|
15176
15234
|
}
|
|
15177
15235
|
function validateTaskOutput(taskType, output, input, runtime) {
|
|
15178
15236
|
return validateTaskResult(taskType, output, input, runtime);
|
|
@@ -15520,6 +15578,10 @@ var TaskAttempt = _Object_({
|
|
|
15520
15578
|
taskId: Uuid,
|
|
15521
15579
|
attemptN: Number$1({ minimum: 1 }),
|
|
15522
15580
|
claimedByAgentId: Uuid,
|
|
15581
|
+
leaseId: Union([Uuid, Null()]),
|
|
15582
|
+
runtimeProfileId: Union([Uuid, Null()]),
|
|
15583
|
+
runtimeProfileRevision: Union([Integer({ minimum: 1 }), Null()]),
|
|
15584
|
+
policySnapshotHash: Union([String$1({ pattern: "^sha256:[0-9a-f]{64}$" }), Null()]),
|
|
15523
15585
|
runtimeId: Union([Uuid, Null()]),
|
|
15524
15586
|
claimedAt: IsoTimestamp,
|
|
15525
15587
|
startedAt: Union([IsoTimestamp, Null()]),
|
|
@@ -17564,7 +17626,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17564
17626
|
var { createRequire: createRequire$1 } = __require("module");
|
|
17565
17627
|
var { existsSync: existsSync$1 } = __require("node:fs");
|
|
17566
17628
|
var getCallers = require_caller();
|
|
17567
|
-
var { join: join$1, isAbsolute, sep } = __require("node:path");
|
|
17629
|
+
var { join: join$1, isAbsolute: isAbsolute$1, sep } = __require("node:path");
|
|
17568
17630
|
var { fileURLToPath } = __require("node:url");
|
|
17569
17631
|
var sleep = require_atomic_sleep();
|
|
17570
17632
|
var onExit = require_on_exit_leak_free();
|
|
@@ -17625,7 +17687,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17625
17687
|
} catch {
|
|
17626
17688
|
return false;
|
|
17627
17689
|
}
|
|
17628
|
-
return isAbsolute(path) && !existsSync$1(path);
|
|
17690
|
+
return isAbsolute$1(path) && !existsSync$1(path);
|
|
17629
17691
|
}
|
|
17630
17692
|
function stripQuotes(value) {
|
|
17631
17693
|
const first = value[0];
|
|
@@ -17728,7 +17790,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17728
17790
|
return buildStream(fixTarget(target), options, worker, sync, name);
|
|
17729
17791
|
function fixTarget(origin) {
|
|
17730
17792
|
origin = bundlerOverrides[origin] || origin;
|
|
17731
|
-
if (isAbsolute(origin) || origin.indexOf("file://") === 0) return origin;
|
|
17793
|
+
if (isAbsolute$1(origin) || origin.indexOf("file://") === 0) return origin;
|
|
17732
17794
|
if (origin === "pino/file") return join$1(__dirname, "..", "file.js");
|
|
17733
17795
|
let fixTarget;
|
|
17734
17796
|
for (const filePath of callers) try {
|
|
@@ -28537,6 +28599,12 @@ var WRAPPERS = new Map([
|
|
|
28537
28599
|
["builtin", spec(0)]
|
|
28538
28600
|
]);
|
|
28539
28601
|
//#endregion
|
|
28602
|
+
//#region ../pi-runtime/src/config.ts
|
|
28603
|
+
/** Resolve Pi's host-side auth/config directory from process configuration. */
|
|
28604
|
+
function resolvePiCodingAgentDir() {
|
|
28605
|
+
return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
|
|
28606
|
+
}
|
|
28607
|
+
//#endregion
|
|
28540
28608
|
//#region ../pi-runtime/src/snapshot.ts
|
|
28541
28609
|
/**
|
|
28542
28610
|
* Snapshot builder with auto-build and caching.
|
|
@@ -28826,6 +28894,17 @@ async function delay(ms, signal, label) {
|
|
|
28826
28894
|
* investigation and the alternatives we rejected.
|
|
28827
28895
|
*/
|
|
28828
28896
|
var GUEST_TASK_CONTEXT_MOUNT = "/moltnet-task-context";
|
|
28897
|
+
function resolveVfsShadowConfig(config) {
|
|
28898
|
+
const patterns = config?.vfs?.shadow ?? [];
|
|
28899
|
+
if (patterns.length === 0) return {
|
|
28900
|
+
mode: "none",
|
|
28901
|
+
patterns: []
|
|
28902
|
+
};
|
|
28903
|
+
return {
|
|
28904
|
+
mode: config?.vfs?.shadowMode ?? "tmpfs",
|
|
28905
|
+
patterns
|
|
28906
|
+
};
|
|
28907
|
+
}
|
|
28829
28908
|
function shouldRunResumeCommand(entry, ctx) {
|
|
28830
28909
|
if (typeof entry === "string") return true;
|
|
28831
28910
|
const workspaceModes = entry.when?.workspaceMode;
|
|
@@ -28898,7 +28977,7 @@ function resolveVmAgentDir(config) {
|
|
|
28898
28977
|
function loadCredentials(agentDir) {
|
|
28899
28978
|
const moltnetJson = readFileSync(path.join(agentDir, "moltnet.json"), "utf8");
|
|
28900
28979
|
const agentEnvRaw = readFileSync(path.join(agentDir, "env"), "utf8");
|
|
28901
|
-
const piAgentDir =
|
|
28980
|
+
const piAgentDir = resolvePiCodingAgentDir();
|
|
28902
28981
|
const piAuthPath = path.join(piAgentDir, "auth.json");
|
|
28903
28982
|
const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
|
|
28904
28983
|
const gitconfigPath = path.join(agentDir, "gitconfig");
|
|
@@ -29053,7 +29132,7 @@ async function resumeVm(config) {
|
|
|
29053
29132
|
else vmAgentEnv[k] = v;
|
|
29054
29133
|
}
|
|
29055
29134
|
vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
|
|
29056
|
-
const vfsConfig = config.sandboxConfig
|
|
29135
|
+
const vfsConfig = resolveVfsShadowConfig(config.sandboxConfig);
|
|
29057
29136
|
let workspaceProvider = new RealFSProvider(config.mountPath);
|
|
29058
29137
|
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
29059
29138
|
shouldShadow: ({ path: shadowPath }) => shouldShadowNodeModulesPath(shadowPath),
|
|
@@ -29061,11 +29140,11 @@ async function resumeVm(config) {
|
|
|
29061
29140
|
tmpfs: new AutoParentMemoryProvider(),
|
|
29062
29141
|
writeMode: "tmpfs"
|
|
29063
29142
|
});
|
|
29064
|
-
if (vfsConfig
|
|
29065
|
-
const predicate = createShadowPathPredicate(vfsConfig.
|
|
29143
|
+
if (vfsConfig.mode !== "none") {
|
|
29144
|
+
const predicate = createShadowPathPredicate(vfsConfig.patterns);
|
|
29066
29145
|
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
29067
29146
|
shouldShadow: predicate,
|
|
29068
|
-
writeMode: vfsConfig.
|
|
29147
|
+
writeMode: vfsConfig.mode
|
|
29069
29148
|
});
|
|
29070
29149
|
}
|
|
29071
29150
|
const forwardedEnv = {};
|
|
@@ -29525,7 +29604,7 @@ async function executeGondolinGrep(vm, localCwd, guestWorkspace, params, signal)
|
|
|
29525
29604
|
const onAbort = () => ac.abort();
|
|
29526
29605
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
29527
29606
|
try {
|
|
29528
|
-
const proc = vm.exec(["
|
|
29607
|
+
const proc = vm.exec(["rg", ...args], {
|
|
29529
29608
|
signal: ac.signal,
|
|
29530
29609
|
stdout: "pipe",
|
|
29531
29610
|
stderr: "pipe"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoltNet pi extension — sandboxed tool execution in Gondolin VMs with MoltNet identity and persistent memory",
|
|
6
6
|
"keywords": [
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@earendil-works/gondolin": "^0.9.1",
|
|
37
|
-
"@themoltnet/pi-runtime": "0.
|
|
37
|
+
"@themoltnet/pi-runtime": "0.5.0",
|
|
38
38
|
"@themoltnet/sdk": "0.128.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@earendil-works/pi-ai": "0.
|
|
41
|
+
"@earendil-works/pi-ai": "0.79.4",
|
|
42
42
|
"@earendil-works/pi-coding-agent": "0.79.4"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@earendil-works/pi-ai": "0.
|
|
53
|
+
"@earendil-works/pi-ai": "0.79.4",
|
|
54
54
|
"@earendil-works/pi-coding-agent": "0.79.4",
|
|
55
55
|
"@types/node": "^22.19.0",
|
|
56
56
|
"typescript": "~5.9.2",
|