@themoltnet/pi-extension 0.29.0 → 0.30.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 +22 -0
- package/dist/index.js +105 -64
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EditOperations } from '@earendil-works/pi-coding-agent';
|
|
|
6
6
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
7
7
|
import { LoadSkillsResult } from '@earendil-works/pi-coding-agent';
|
|
8
8
|
import { Model } from '@earendil-works/pi-ai';
|
|
9
|
+
import { Readable } from 'node:stream';
|
|
9
10
|
import { ReadOperations } from '@earendil-works/pi-coding-agent';
|
|
10
11
|
import { Skill } from '@earendil-works/pi-coding-agent';
|
|
11
12
|
import { Static } from 'typebox';
|
|
@@ -294,6 +295,8 @@ export declare interface ExecutePiTaskOptions {
|
|
|
294
295
|
extraAllowedHosts?: string[];
|
|
295
296
|
/** Sandbox overrides (env, VFS shadows, resources). */
|
|
296
297
|
sandboxConfig?: SandboxConfig;
|
|
298
|
+
/** Host environment variable names to forward into the Pi VM. */
|
|
299
|
+
forwardEnv?: string[];
|
|
297
300
|
/**
|
|
298
301
|
* Forwarded to `buildTaskUserPrompt` for per-type builders. Static
|
|
299
302
|
* across tasks. Today no built-in builder needs per-task `extras` —
|
|
@@ -481,6 +484,17 @@ declare interface MoltNetToolsConfig {
|
|
|
481
484
|
clearSessionErrors(): void;
|
|
482
485
|
/** Host working directory for host-exec commands (worktree path or cwd). */
|
|
483
486
|
getHostCwd?(): string;
|
|
487
|
+
/**
|
|
488
|
+
* Optional workspace-file reader. Daemon/Gondolin callers provide this so
|
|
489
|
+
* artifact uploads see guest overlay writes that may not exist on the host
|
|
490
|
+
* mount path yet.
|
|
491
|
+
*/
|
|
492
|
+
openWorkspaceFileForRead?(filePath: string): Promise<{
|
|
493
|
+
stream: Readable;
|
|
494
|
+
isFile: boolean;
|
|
495
|
+
sizeBytes?: number;
|
|
496
|
+
displayPath?: string;
|
|
497
|
+
}>;
|
|
484
498
|
/**
|
|
485
499
|
* Set of process.env keys that are safe to forward to host-exec child
|
|
486
500
|
* processes. Configured at sandbox startup so the caller can include
|
|
@@ -972,6 +986,14 @@ export declare interface VmConfig {
|
|
|
972
986
|
extraAllowedHosts?: string[];
|
|
973
987
|
/** Full sandbox config (vfs shadows, env overrides). */
|
|
974
988
|
sandboxConfig?: SandboxConfig;
|
|
989
|
+
/**
|
|
990
|
+
* Host environment variable names to copy into the VM process.
|
|
991
|
+
*
|
|
992
|
+
* Runtime profiles use this for provider API keys: `requiredEnv` proves the
|
|
993
|
+
* daemon host has the secret, and this allowlist forwards only those names
|
|
994
|
+
* into the guest without storing secret values in the profile.
|
|
995
|
+
*/
|
|
996
|
+
forwardEnv?: string[];
|
|
975
997
|
/** Abort resume/setup work, closing any live VM owned by resumeVm. */
|
|
976
998
|
signal?: AbortSignal;
|
|
977
999
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
4
|
-
import path, { join, relative, sep } from "node:path";
|
|
4
|
+
import path, { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { DEFAULT_MAX_BYTES, DefaultResourceLoader, SessionManager, createAgentSession, createBashTool, createBashToolDefinition, createEditTool, createEditToolDefinition, createFindTool, createFindToolDefinition, createGrepTool, createGrepToolDefinition, createLsTool, createLsToolDefinition, createReadTool, createReadToolDefinition, createSyntheticSourceInfo, createWriteTool, createWriteToolDefinition, defineTool, formatSize, parseFrontmatter, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
@@ -2874,6 +2874,15 @@ function unwrapResult(result) {
|
|
|
2874
2874
|
networkError.stack = error.stack;
|
|
2875
2875
|
throw networkError;
|
|
2876
2876
|
}
|
|
2877
|
+
const responseSummary = summarizeResponse(result.response);
|
|
2878
|
+
if (responseSummary) {
|
|
2879
|
+
const detail = stringifyUnknown(error);
|
|
2880
|
+
throw new MoltNetError(`MoltNet API request failed with HTTP ${responseSummary.status} ${responseSummary.statusText}: ${detail}`, {
|
|
2881
|
+
code: `HTTP_${responseSummary.status}`,
|
|
2882
|
+
detail,
|
|
2883
|
+
statusCode: responseSummary.status
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2877
2886
|
throw new MoltNetError(`Unexpected error from MoltNet API: ${stringifyUnknown(error)}`, { code: "UNKNOWN" });
|
|
2878
2887
|
}
|
|
2879
2888
|
if (result.data === void 0) throw new MoltNetError("Unexpected empty response from MoltNet API", { code: "EMPTY_RESPONSE" });
|
|
@@ -2891,6 +2900,15 @@ function stringifyUnknown(value) {
|
|
|
2891
2900
|
return String(value);
|
|
2892
2901
|
}
|
|
2893
2902
|
}
|
|
2903
|
+
function summarizeResponse(response) {
|
|
2904
|
+
if (!response || typeof response !== "object") return null;
|
|
2905
|
+
const candidate = response;
|
|
2906
|
+
if (typeof candidate.status !== "number") return null;
|
|
2907
|
+
return {
|
|
2908
|
+
status: candidate.status,
|
|
2909
|
+
statusText: typeof candidate.statusText === "string" && candidate.statusText ? candidate.statusText : "Error"
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2894
2912
|
function unwrapRequired(result, message, code) {
|
|
2895
2913
|
if (result.error || !result.data) throw new MoltNetError(message, { code });
|
|
2896
2914
|
return result.data;
|
|
@@ -17542,11 +17560,34 @@ function shouldAutoApproveHostExec(params, config) {
|
|
|
17542
17560
|
}
|
|
17543
17561
|
async function resolveWorkspaceFilePath(cwd, filePath) {
|
|
17544
17562
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
17545
|
-
const
|
|
17563
|
+
const realCwd = await realpath(cwd);
|
|
17564
|
+
let realResolved;
|
|
17565
|
+
try {
|
|
17566
|
+
realResolved = await realpath(resolved);
|
|
17567
|
+
} catch (err) {
|
|
17568
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`task artifact input path does not exist: ${filePath}. Write the file before calling moltnet_upload_task_artifact.`);
|
|
17569
|
+
throw err;
|
|
17570
|
+
}
|
|
17546
17571
|
const rel = path.relative(realCwd, realResolved);
|
|
17547
17572
|
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`task artifact path escapes workspace: ${filePath}`);
|
|
17548
17573
|
return realResolved;
|
|
17549
17574
|
}
|
|
17575
|
+
async function openWorkspaceArtifactInput(config, cwd, filePath) {
|
|
17576
|
+
if (config.openWorkspaceFileForRead) try {
|
|
17577
|
+
return await config.openWorkspaceFileForRead(filePath);
|
|
17578
|
+
} catch (err) {
|
|
17579
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`task artifact input path does not exist: ${filePath}. Write the file before calling moltnet_upload_task_artifact.`);
|
|
17580
|
+
throw err;
|
|
17581
|
+
}
|
|
17582
|
+
const resolved = await resolveWorkspaceFilePath(cwd, filePath);
|
|
17583
|
+
const info = await stat(resolved);
|
|
17584
|
+
return {
|
|
17585
|
+
stream: createReadStream(resolved),
|
|
17586
|
+
isFile: info.isFile(),
|
|
17587
|
+
sizeBytes: info.size,
|
|
17588
|
+
displayPath: path.relative(cwd, resolved)
|
|
17589
|
+
};
|
|
17590
|
+
}
|
|
17550
17591
|
async function resolveWorkspaceOutputPath(cwd, filePath) {
|
|
17551
17592
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
17552
17593
|
const [realCwd, realParent] = await Promise.all([realpath(cwd), realpath(path.dirname(resolved))]);
|
|
@@ -18054,14 +18095,12 @@ function createMoltNetTools(config) {
|
|
|
18054
18095
|
if (!teamId) throw new Error("moltnet_upload_task_artifact requires a team context");
|
|
18055
18096
|
const taskCtx = config.getTaskContext?.() ?? null;
|
|
18056
18097
|
if (!taskCtx) throw new Error("moltnet_upload_task_artifact is only available during an active task attempt");
|
|
18057
|
-
const
|
|
18058
|
-
|
|
18059
|
-
const info = await stat(resolved);
|
|
18060
|
-
if (!info.isFile()) throw new Error(`task artifact path is not a file: ${params.filePath}`);
|
|
18098
|
+
const input = await openWorkspaceArtifactInput(config, config.getHostCwd?.() ?? process.cwd(), params.filePath);
|
|
18099
|
+
if (!input.isFile) throw new Error(`task artifact path is not a file: ${params.filePath}`);
|
|
18061
18100
|
const artifact = await agent.tasks.artifacts.upload({
|
|
18062
18101
|
taskId: taskCtx.taskId,
|
|
18063
18102
|
attemptN: taskCtx.attemptN
|
|
18064
|
-
},
|
|
18103
|
+
}, input.stream, {
|
|
18065
18104
|
kind: params.kind,
|
|
18066
18105
|
title: params.title,
|
|
18067
18106
|
contentType: params.contentType ?? "application/octet-stream",
|
|
@@ -18072,8 +18111,8 @@ function createMoltNetTools(config) {
|
|
|
18072
18111
|
type: "text",
|
|
18073
18112
|
text: JSON.stringify({
|
|
18074
18113
|
...artifact,
|
|
18075
|
-
filePath:
|
|
18076
|
-
localSizeBytes:
|
|
18114
|
+
filePath: input.displayPath ?? params.filePath,
|
|
18115
|
+
localSizeBytes: input.sizeBytes ?? null
|
|
18077
18116
|
}, null, 2)
|
|
18078
18117
|
}],
|
|
18079
18118
|
details: {}
|
|
@@ -18657,13 +18696,13 @@ async function delay(ms, signal, label) {
|
|
|
18657
18696
|
//#endregion
|
|
18658
18697
|
//#region src/vm-manager.ts
|
|
18659
18698
|
/**
|
|
18660
|
-
* Memory-backed VFS mount used by the daemon to inject task
|
|
18661
|
-
*
|
|
18662
|
-
*
|
|
18663
|
-
*
|
|
18699
|
+
* Memory-backed VFS mount used by the daemon to inject task context
|
|
18700
|
+
* (#943 slice 1.5). This is a separate top-level mount because Gondolin
|
|
18701
|
+
* mounts can't nest. The agent's Gondolin-bound Read tool accepts paths
|
|
18702
|
+
* under this prefix (see toGuestPath in tool-operations.ts).
|
|
18664
18703
|
*
|
|
18665
18704
|
* Why MemoryProvider rather than a path under the workspace mount:
|
|
18666
|
-
* - Injected
|
|
18705
|
+
* - Injected task context is ephemeral by intent: per-task-attempt input
|
|
18667
18706
|
* scoped to the VM lifetime. MemoryProvider models that exactly —
|
|
18668
18707
|
* in-memory, per-VM-instance, zero host artefacts, automatic
|
|
18669
18708
|
* cleanup on VM close.
|
|
@@ -18676,7 +18715,7 @@ async function delay(ms, signal, label) {
|
|
|
18676
18715
|
* and episodic 7affbfeb-18a2-4963-aeac-c177eb2afa2d for the full
|
|
18677
18716
|
* investigation and the alternatives we rejected.
|
|
18678
18717
|
*/
|
|
18679
|
-
var
|
|
18718
|
+
var GUEST_TASK_CONTEXT_MOUNT = "/moltnet-task-context";
|
|
18680
18719
|
function shouldRunResumeCommand(entry, ctx) {
|
|
18681
18720
|
if (typeof entry === "string") return true;
|
|
18682
18721
|
const workspaceModes = entry.when?.workspaceMode;
|
|
@@ -18840,10 +18879,17 @@ async function resumeVm(config) {
|
|
|
18840
18879
|
writeMode: vfsConfig.shadowMode ?? "tmpfs"
|
|
18841
18880
|
});
|
|
18842
18881
|
}
|
|
18882
|
+
const forwardedEnv = {};
|
|
18883
|
+
for (const name of config.forwardEnv ?? []) {
|
|
18884
|
+
const value = process.env[name];
|
|
18885
|
+
if (value === void 0 || value === "") continue;
|
|
18886
|
+
forwardedEnv[name] = value;
|
|
18887
|
+
}
|
|
18843
18888
|
const envOverrides = config.sandboxConfig?.env ?? {};
|
|
18844
18889
|
const vmEnv = {
|
|
18845
18890
|
...secretEnv,
|
|
18846
18891
|
...vmAgentEnv,
|
|
18892
|
+
...forwardedEnv,
|
|
18847
18893
|
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go/bin",
|
|
18848
18894
|
HOME: "/home/agent",
|
|
18849
18895
|
NODE_NO_WARNINGS: "1",
|
|
@@ -18861,7 +18907,7 @@ async function resumeVm(config) {
|
|
|
18861
18907
|
...resources?.cpus && { cpus: resources.cpus },
|
|
18862
18908
|
vfs: { mounts: {
|
|
18863
18909
|
[guestWorkspace]: workspaceProvider,
|
|
18864
|
-
[
|
|
18910
|
+
[GUEST_TASK_CONTEXT_MOUNT]: new MemoryProvider()
|
|
18865
18911
|
} }
|
|
18866
18912
|
}),
|
|
18867
18913
|
signal: config.signal,
|
|
@@ -19060,9 +19106,9 @@ function toHostToolPath(localCwd, guestWorkspace, guestPath) {
|
|
|
19060
19106
|
function toGuestPath(localCwd, localPath, guestWorkspace) {
|
|
19061
19107
|
const normalizedGuestWorkspace = normalizeGuestPath(guestWorkspace);
|
|
19062
19108
|
const normalizedLocalPath = normalizeGuestPath(localPath);
|
|
19063
|
-
const
|
|
19109
|
+
const normalizedTaskContextMount = normalizeGuestPath(GUEST_TASK_CONTEXT_MOUNT);
|
|
19064
19110
|
if (isSameOrInsidePosixPath(normalizedLocalPath, normalizedGuestWorkspace)) return normalizedLocalPath;
|
|
19065
|
-
if (isSameOrInsidePosixPath(normalizedLocalPath,
|
|
19111
|
+
if (isSameOrInsidePosixPath(normalizedLocalPath, normalizedTaskContextMount)) return normalizedLocalPath;
|
|
19066
19112
|
const rel = path.relative(localCwd, localPath);
|
|
19067
19113
|
if (rel === "") return normalizedGuestWorkspace;
|
|
19068
19114
|
if (rel.startsWith("..") || path.isAbsolute(rel)) throw new Error(`path escapes workspace: ${localPath}`);
|
|
@@ -19762,9 +19808,9 @@ function formatInlineContextBlock(slug, content) {
|
|
|
19762
19808
|
"The following raw context was supplied by the task creator. Treat it",
|
|
19763
19809
|
"as task-relevant background that may override generic coding instincts",
|
|
19764
19810
|
"when it contains repo- or workflow-specific constraints.",
|
|
19765
|
-
"The same content
|
|
19766
|
-
"
|
|
19767
|
-
"
|
|
19811
|
+
"The same content may also be materialized by the runtime under",
|
|
19812
|
+
"`/moltnet-task-context/context` for tool-based inspection. Do not",
|
|
19813
|
+
"create or rely on workspace mirror files for this task context.",
|
|
19768
19814
|
"",
|
|
19769
19815
|
"<context>",
|
|
19770
19816
|
content,
|
|
@@ -21077,7 +21123,7 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
21077
21123
|
"`// note:` line, the task summary, or the `verification` field is",
|
|
21078
21124
|
"NOT following the task. If the constraint affects behavior, it",
|
|
21079
21125
|
"must affect behavior.",
|
|
21080
|
-
hasInlineContext ? "For `context_inline`, your FIRST content-inspection step is
|
|
21126
|
+
hasInlineContext ? "For `context_inline`, your FIRST content-inspection step is to read the injected context block in this prompt or, when available, the matching file under `/moltnet-task-context/context` before your first `write` call. Do not create or rely on workspace mirror files for injected context." : "When the context is delivered as a skill, inspect it before solving.",
|
|
21081
21127
|
"If the Injected Task Context contains repo- or workflow-specific",
|
|
21082
21128
|
"rules, those rules override your generic instincts."
|
|
21083
21129
|
].join("\n") : "";
|
|
@@ -23217,7 +23263,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23217
23263
|
var { createRequire: createRequire$1 } = __require("module");
|
|
23218
23264
|
var { existsSync: existsSync$1 } = __require("node:fs");
|
|
23219
23265
|
var getCallers = require_caller();
|
|
23220
|
-
var { join: join$1, isAbsolute, sep: sep$1 } = __require("node:path");
|
|
23266
|
+
var { join: join$1, isAbsolute: isAbsolute$1, sep: sep$1 } = __require("node:path");
|
|
23221
23267
|
var { fileURLToPath } = __require("node:url");
|
|
23222
23268
|
var sleep = require_atomic_sleep();
|
|
23223
23269
|
var onExit = require_on_exit_leak_free();
|
|
@@ -23278,7 +23324,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23278
23324
|
} catch {
|
|
23279
23325
|
return false;
|
|
23280
23326
|
}
|
|
23281
|
-
return isAbsolute(path) && !existsSync$1(path);
|
|
23327
|
+
return isAbsolute$1(path) && !existsSync$1(path);
|
|
23282
23328
|
}
|
|
23283
23329
|
function stripQuotes(value) {
|
|
23284
23330
|
const first = value[0];
|
|
@@ -23381,7 +23427,7 @@ var require_transport = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
23381
23427
|
return buildStream(fixTarget(target), options, worker, sync, name);
|
|
23382
23428
|
function fixTarget(origin) {
|
|
23383
23429
|
origin = bundlerOverrides[origin] || origin;
|
|
23384
|
-
if (isAbsolute(origin) || origin.indexOf("file://") === 0) return origin;
|
|
23430
|
+
if (isAbsolute$1(origin) || origin.indexOf("file://") === 0) return origin;
|
|
23385
23431
|
if (origin === "pino/file") return join$1(__dirname, "..", "file.js");
|
|
23386
23432
|
let fixTarget;
|
|
23387
23433
|
for (const filePath of callers) try {
|
|
@@ -24732,20 +24778,21 @@ var require_multistream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
24732
24778
|
* system prompt; the agent fetches the body on demand via the
|
|
24733
24779
|
* Read tool.
|
|
24734
24780
|
*
|
|
24735
|
-
*
|
|
24736
|
-
* `<available_skills>` metadata (name, description, location), never the
|
|
24781
|
+
* Task-context files are written into a memory-backed VM mount. pi only reads
|
|
24782
|
+
* `<available_skills>` metadata (name, description, location), never the skill
|
|
24737
24783
|
* body, so we construct synthetic `Skill` objects pointing at the in-VM path
|
|
24738
24784
|
* without ever materialising the file on the host.
|
|
24739
24785
|
*/
|
|
24740
24786
|
/**
|
|
24741
|
-
* Where in the VM we write
|
|
24787
|
+
* Where in the VM we write task-context bodies — the memory-backed mount
|
|
24742
24788
|
* declared in `vm-manager.ts`. See the comment on
|
|
24743
|
-
* `
|
|
24744
|
-
*
|
|
24745
|
-
*
|
|
24746
|
-
*
|
|
24789
|
+
* `GUEST_TASK_CONTEXT_MOUNT` there for the full rationale (ephemeral by
|
|
24790
|
+
* intent + the worktree symlink interaction with Gondolin's sandbox-escape
|
|
24791
|
+
* protection). The agent's Gondolin Read tool accepts paths under this mount
|
|
24792
|
+
* via `toGuestPath` in `tool-operations.ts`.
|
|
24747
24793
|
*/
|
|
24748
|
-
var SKILL_ROOT_IN_VM =
|
|
24794
|
+
var SKILL_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/skills`;
|
|
24795
|
+
var INLINE_CONTEXT_ROOT_IN_VM = `${GUEST_TASK_CONTEXT_MOUNT}/context`;
|
|
24749
24796
|
/** Bounds borrowed from pi's skill validation; conservative caps so a
|
|
24750
24797
|
* malformed SKILL.md doesn't bloat the system prompt. */
|
|
24751
24798
|
var MAX_SKILL_NAME = 64;
|
|
@@ -24756,13 +24803,7 @@ var MAX_SKILL_DESCRIPTION = 1024;
|
|
|
24756
24803
|
*/
|
|
24757
24804
|
async function injectTaskContext(args) {
|
|
24758
24805
|
const skills = [];
|
|
24759
|
-
|
|
24760
|
-
const { guestWorkspace } = args;
|
|
24761
|
-
const inlineContextRoot = `${guestWorkspace}/.moltnet/context`;
|
|
24762
|
-
const workspaceContextPack = `${guestWorkspace}/context-pack.md`;
|
|
24763
|
-
const workspaceAgentsMd = `${guestWorkspace}/AGENTS.md`;
|
|
24764
|
-
const workspaceClaudeDir = `${guestWorkspace}/.claude`;
|
|
24765
|
-
const workspaceClaudeMd = `${workspaceClaudeDir}/CLAUDE.md`;
|
|
24806
|
+
args.guestWorkspace;
|
|
24766
24807
|
const resolved = await resolveTaskContext({
|
|
24767
24808
|
context: args.context,
|
|
24768
24809
|
deliver: {
|
|
@@ -24779,23 +24820,12 @@ async function injectTaskContext(args) {
|
|
|
24779
24820
|
}));
|
|
24780
24821
|
},
|
|
24781
24822
|
contextFile: async ({ suggestedFileName, content }) => {
|
|
24782
|
-
await args.fs.mkdir(
|
|
24783
|
-
const filePath = `${
|
|
24823
|
+
await args.fs.mkdir(INLINE_CONTEXT_ROOT_IN_VM, { recursive: true });
|
|
24824
|
+
const filePath = `${INLINE_CONTEXT_ROOT_IN_VM}/${suggestedFileName}`;
|
|
24784
24825
|
await args.fs.writeFile(filePath, content, { mode: 420 });
|
|
24785
|
-
inlineContexts.push({
|
|
24786
|
-
slug: suggestedFileName.replace(/\.md$/u, ""),
|
|
24787
|
-
content
|
|
24788
|
-
});
|
|
24789
24826
|
}
|
|
24790
24827
|
}
|
|
24791
24828
|
});
|
|
24792
|
-
if (inlineContexts.length > 0) {
|
|
24793
|
-
const packContent = buildWorkspaceContextPack(inlineContexts);
|
|
24794
|
-
await args.fs.writeFile(workspaceContextPack, packContent, { mode: 420 });
|
|
24795
|
-
await args.fs.writeFile(workspaceAgentsMd, packContent, { mode: 420 });
|
|
24796
|
-
await args.fs.mkdir(workspaceClaudeDir, { recursive: true });
|
|
24797
|
-
await args.fs.writeFile(workspaceClaudeMd, "@../context-pack.md\n", { mode: 420 });
|
|
24798
|
-
}
|
|
24799
24829
|
return {
|
|
24800
24830
|
injected: resolved.injected,
|
|
24801
24831
|
skills,
|
|
@@ -24803,17 +24833,6 @@ async function injectTaskContext(args) {
|
|
|
24803
24833
|
userInlineSuffix: resolved.userInlineSuffix
|
|
24804
24834
|
};
|
|
24805
24835
|
}
|
|
24806
|
-
function buildWorkspaceContextPack(contexts) {
|
|
24807
|
-
return [
|
|
24808
|
-
"# Context Pack",
|
|
24809
|
-
"",
|
|
24810
|
-
...contexts.map(({ slug, content }) => [
|
|
24811
|
-
`## ${slug}`,
|
|
24812
|
-
"",
|
|
24813
|
-
content.trimEnd()
|
|
24814
|
-
].join("\n"))
|
|
24815
|
-
].join("\n\n").trimEnd() + "\n";
|
|
24816
|
-
}
|
|
24817
24836
|
/**
|
|
24818
24837
|
* Build a `Skill` object pi will faithfully render in
|
|
24819
24838
|
* `<available_skills>`. We extract `name` and `description` from the
|
|
@@ -25527,6 +25546,17 @@ function shouldSkipSeedEntry(sourceEntry, entryName, resolvedTargetDir) {
|
|
|
25527
25546
|
* `AgentRuntime`.
|
|
25528
25547
|
*/
|
|
25529
25548
|
var noopTurnEventHandler = () => {};
|
|
25549
|
+
async function openVmWorkspaceFileForRead(config) {
|
|
25550
|
+
const localPath = isAbsolute(config.filePath) ? config.filePath : resolve(config.cwdPath, config.filePath);
|
|
25551
|
+
const guestPath = toGuestPath(config.cwdPath, localPath, config.guestWorkspace);
|
|
25552
|
+
const info = await config.vm.fs.stat(guestPath);
|
|
25553
|
+
return {
|
|
25554
|
+
stream: await config.vm.fs.readFileStream(guestPath),
|
|
25555
|
+
isFile: info.isFile(),
|
|
25556
|
+
sizeBytes: typeof info.size === "number" ? info.size : void 0,
|
|
25557
|
+
displayPath: config.filePath
|
|
25558
|
+
};
|
|
25559
|
+
}
|
|
25530
25560
|
function createGondolinToolDefinitions(config) {
|
|
25531
25561
|
const { vm, mountPath, guestWorkspace } = config;
|
|
25532
25562
|
const grepTool = createGrepToolDefinition(mountPath);
|
|
@@ -25703,6 +25733,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25703
25733
|
workspaceMode: workspace.mode,
|
|
25704
25734
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
25705
25735
|
sandboxConfig,
|
|
25736
|
+
forwardEnv: opts.forwardEnv,
|
|
25706
25737
|
signal: reporter.cancelSignal
|
|
25707
25738
|
});
|
|
25708
25739
|
} catch (err) {
|
|
@@ -25718,9 +25749,11 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25718
25749
|
const taskTeamId = task.teamId ?? "";
|
|
25719
25750
|
activateAgentEnv(managed.credentials.agentEnv, agentRootDir);
|
|
25720
25751
|
const activeWorkspace = workspace;
|
|
25752
|
+
const activeManaged = managed;
|
|
25721
25753
|
if (!activeWorkspace) throw new Error("task workspace not prepared");
|
|
25722
25754
|
await emit("info", {
|
|
25723
25755
|
event: "execute_start",
|
|
25756
|
+
correlationId: task.correlationId ?? null,
|
|
25724
25757
|
taskType: task.taskType,
|
|
25725
25758
|
teamId: task.teamId,
|
|
25726
25759
|
provider: opts.provider,
|
|
@@ -25773,6 +25806,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25773
25806
|
taskPrompt = assembled.text;
|
|
25774
25807
|
await emit("info", {
|
|
25775
25808
|
event: "prompt_assembled",
|
|
25809
|
+
correlationId: task.correlationId ?? null,
|
|
25776
25810
|
taskType: assembled.taskType,
|
|
25777
25811
|
sections: assembled.trace
|
|
25778
25812
|
});
|
|
@@ -25804,6 +25838,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25804
25838
|
}
|
|
25805
25839
|
if (injectedContext.injected.length > 0) await emit("info", {
|
|
25806
25840
|
event: "context_injected",
|
|
25841
|
+
correlationId: task.correlationId ?? null,
|
|
25807
25842
|
count: injectedContext.injected.length,
|
|
25808
25843
|
bindings: injectedContext.injected.map((r) => r.binding),
|
|
25809
25844
|
slugs: injectedContext.injected.map((r) => r.slug)
|
|
@@ -25828,6 +25863,12 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
25828
25863
|
getSessionErrors: () => [],
|
|
25829
25864
|
clearSessionErrors: () => {},
|
|
25830
25865
|
getHostCwd: () => cwdPath,
|
|
25866
|
+
openWorkspaceFileForRead: (filePath) => openVmWorkspaceFileForRead({
|
|
25867
|
+
vm: activeManaged.vm,
|
|
25868
|
+
cwdPath,
|
|
25869
|
+
guestWorkspace: activeManaged.guestWorkspace,
|
|
25870
|
+
filePath
|
|
25871
|
+
}),
|
|
25831
25872
|
hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
|
|
25832
25873
|
hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
|
|
25833
25874
|
getTaskContext: () => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/pi-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.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": [
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@earendil-works/gondolin": "^0.9.1",
|
|
37
37
|
"@opentelemetry/api": "^1.9.0",
|
|
38
38
|
"typebox": "^1.2.8",
|
|
39
|
-
"@themoltnet/agent-runtime": "0.
|
|
40
|
-
"@themoltnet/sdk": "0.
|
|
39
|
+
"@themoltnet/agent-runtime": "0.33.0",
|
|
40
|
+
"@themoltnet/sdk": "0.116.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|