@themoltnet/pi-runtime 0.4.0 → 0.5.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 +54 -20
- package/dist/index.js +494 -525
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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 { readFile, realpath, stat } from "node:fs/promises";
|
|
4
|
+
import { mkdir, readFile, realpath, stat } from "node:fs/promises";
|
|
5
5
|
import path, { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { pipeline } from "node:stream/promises";
|
|
7
|
-
import { Type
|
|
8
|
-
import { DEFAULT_MAX_BYTES, DefaultResourceLoader, SessionManager, createAgentSession, createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createSyntheticSourceInfo, createWriteToolDefinition, defineTool, formatSize, parseFrontmatter, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
8
|
+
import { AuthStorage, DEFAULT_MAX_BYTES, DefaultResourceLoader, ModelRegistry, SessionManager, createAgentSession, createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createSyntheticSourceInfo, createWriteToolDefinition, defineTool, formatSize, parseFrontmatter, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
10
|
-
import { homedir } from "node:os";
|
|
11
10
|
import crypto, { createHash } from "crypto";
|
|
12
11
|
import { createHash as createHash$1 } from "node:crypto";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { Readable } from "node:stream";
|
|
15
15
|
import { MemoryProvider, RealFSProvider, ShadowProvider, VM, VmCheckpoint, createHttpHooks, createShadowPathPredicate, ensureImageSelector, isWriteFlag, loadGuestAssets } from "@earendil-works/gondolin";
|
|
@@ -43,6 +43,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
43
43
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp$1({}, "__esModule", { value: true }), mod);
|
|
44
44
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
45
45
|
//#endregion
|
|
46
|
+
//#region src/path-containment.ts
|
|
47
|
+
/**
|
|
48
|
+
* Check containment for already-resolved lexical or real paths.
|
|
49
|
+
*
|
|
50
|
+
* Callers that accept untrusted paths must resolve/realpath at their I/O
|
|
51
|
+
* boundary first; keeping the platform-specific relative-path rule here avoids
|
|
52
|
+
* subtly different `..` and absolute-path handling across runtime cleanup,
|
|
53
|
+
* session sync, and artifact staging.
|
|
54
|
+
*/
|
|
55
|
+
function isResolvedPathInsideRoot(path, root) {
|
|
56
|
+
const rel = relative(root, path);
|
|
57
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
46
60
|
//#region src/moltnet/render-phase6.ts
|
|
47
61
|
function slugToTitle(value) {
|
|
48
62
|
return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -255,11 +269,32 @@ async function openWorkspaceArtifactInput(config, cwd, filePath) {
|
|
|
255
269
|
}
|
|
256
270
|
async function resolveWorkspaceOutputPath(cwd, filePath) {
|
|
257
271
|
const resolved = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
272
|
+
const workspaceRoot = path.resolve(cwd);
|
|
273
|
+
const lexicalRel = path.relative(workspaceRoot, resolved);
|
|
274
|
+
if (lexicalRel === "" || lexicalRel.startsWith("..") || path.isAbsolute(lexicalRel)) throw new Error(`task artifact output path escapes workspace: ${filePath}`);
|
|
275
|
+
const realCwd = await realpath(cwd);
|
|
276
|
+
const parent = path.dirname(resolved);
|
|
277
|
+
assertPathInsideWorkspace(realCwd, await findExistingAncestor(parent), filePath);
|
|
278
|
+
await mkdir(parent, { recursive: true });
|
|
279
|
+
assertPathInsideWorkspace(realCwd, await realpath(parent), filePath);
|
|
261
280
|
return resolved;
|
|
262
281
|
}
|
|
282
|
+
async function findExistingAncestor(candidate) {
|
|
283
|
+
let current = candidate;
|
|
284
|
+
for (;;) {
|
|
285
|
+
try {
|
|
286
|
+
return await realpath(current);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (!err || typeof err !== "object" || !("code" in err) || err.code !== "ENOENT") throw err;
|
|
289
|
+
}
|
|
290
|
+
const parent = path.dirname(current);
|
|
291
|
+
if (parent === current) throw new Error(`task artifact output has no existing ancestor: ${candidate}`);
|
|
292
|
+
current = parent;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function assertPathInsideWorkspace(realCwd, realPath, displayPath) {
|
|
296
|
+
if (!isResolvedPathInsideRoot(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
|
|
297
|
+
}
|
|
263
298
|
/**
|
|
264
299
|
* Expand the `taskFilter` shorthand on the diary list/search tools into
|
|
265
300
|
* the matching `task:*` provenance tags emitted by `moltnet_create_entry`
|
|
@@ -1258,6 +1293,7 @@ async function buildAgentSession(args) {
|
|
|
1258
1293
|
agentDir: args.piAuthDir,
|
|
1259
1294
|
cwd: args.cwdPath,
|
|
1260
1295
|
model: args.modelHandle,
|
|
1296
|
+
...args.modelRegistry ? { modelRegistry: args.modelRegistry } : {},
|
|
1261
1297
|
thinkingLevel: args.thinkingLevel ?? void 0,
|
|
1262
1298
|
tools: args.tools,
|
|
1263
1299
|
customTools: args.customTools,
|
|
@@ -9377,6 +9413,11 @@ var RuntimeModel = _Object_({
|
|
|
9377
9413
|
var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
9378
9414
|
version: 1,
|
|
9379
9415
|
fragments: {
|
|
9416
|
+
"artifact-planner-v1": {
|
|
9417
|
+
binding: "prompt_prefix",
|
|
9418
|
+
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.",
|
|
9419
|
+
slug: "artifact-planner-v1"
|
|
9420
|
+
},
|
|
9380
9421
|
"accountable-delivery-v1": {
|
|
9381
9422
|
binding: "prompt_prefix",
|
|
9382
9423
|
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.",
|
|
@@ -9409,6 +9450,10 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
9409
9450
|
}
|
|
9410
9451
|
},
|
|
9411
9452
|
recipes: {
|
|
9453
|
+
"artifact-planner@v1": {
|
|
9454
|
+
description: "Minimal artifact-only context for bounded semantic classification and planning.",
|
|
9455
|
+
fragments: ["artifact-planner-v1"]
|
|
9456
|
+
},
|
|
9412
9457
|
"run-eval-direct@v1": {
|
|
9413
9458
|
description: "Minimal direct context for a short, isolated evaluation run.",
|
|
9414
9459
|
fragments: ["run-eval-direct-v1"]
|
|
@@ -10896,7 +10941,7 @@ var VerificationResult = _Object_({
|
|
|
10896
10941
|
var VerificationRecord = _Object_({
|
|
10897
10942
|
inputCid: String$1({ minLength: 1 }),
|
|
10898
10943
|
results: _Array_(VerificationResult),
|
|
10899
|
-
passed: Boolean$1()
|
|
10944
|
+
passed: Boolean$1({ description: "True iff every verification result has status \"pass\" or \"skip\"; false when any result has status \"fail\"." })
|
|
10900
10945
|
}, {
|
|
10901
10946
|
$id: "VerificationRecord",
|
|
10902
10947
|
additionalProperties: false
|
|
@@ -14590,6 +14635,16 @@ function checkVerificationInputCid(value, runtime) {
|
|
|
14590
14635
|
}];
|
|
14591
14636
|
return [];
|
|
14592
14637
|
}
|
|
14638
|
+
function checkVerificationPassedConsistency(value) {
|
|
14639
|
+
const verification = value !== null && typeof value === "object" ? value.verification : void 0;
|
|
14640
|
+
if (verification === void 0 || !Array.isArray(verification.results) || typeof verification.passed !== "boolean") return [];
|
|
14641
|
+
const expectedPassed = verification.results.every((result) => result.status !== "fail");
|
|
14642
|
+
if (verification.passed !== expectedPassed) return [{
|
|
14643
|
+
field: "output/verification/passed",
|
|
14644
|
+
message: "must be true iff no verification result has status \"fail\""
|
|
14645
|
+
}];
|
|
14646
|
+
return [];
|
|
14647
|
+
}
|
|
14593
14648
|
function validateTaskResult(taskType, value, input, runtime, submission = false) {
|
|
14594
14649
|
const entry = getTaskTypeEntry(taskType);
|
|
14595
14650
|
if (!entry) return [{
|
|
@@ -14605,7 +14660,7 @@ function validateTaskResult(taskType, value, input, runtime, submission = false)
|
|
|
14605
14660
|
message: validationError
|
|
14606
14661
|
}];
|
|
14607
14662
|
}
|
|
14608
|
-
return checkVerificationInputCid(value, runtime);
|
|
14663
|
+
return [...checkVerificationInputCid(value, runtime), ...checkVerificationPassedConsistency(value)];
|
|
14609
14664
|
}
|
|
14610
14665
|
function validateTaskOutput(taskType, output, input, runtime) {
|
|
14611
14666
|
return validateTaskResult(taskType, output, input, runtime);
|
|
@@ -15155,69 +15210,6 @@ function assembleTaskPrompt(taskType, sections) {
|
|
|
15155
15210
|
};
|
|
15156
15211
|
}
|
|
15157
15212
|
//#endregion
|
|
15158
|
-
//#region ../agent-runtime/src/prompts/final-output.ts
|
|
15159
|
-
function buildFinalOutputBlock(opts) {
|
|
15160
|
-
const { taskType, outputSchemaName, shapeSketch, extraNotes } = opts;
|
|
15161
|
-
const submitTool = submitOutputToolName(taskType);
|
|
15162
|
-
const lines = [
|
|
15163
|
-
"## Final output (read this carefully)",
|
|
15164
|
-
"",
|
|
15165
|
-
`Your VERY LAST action in this conversation MUST report the structured`,
|
|
15166
|
-
`output matching \`${outputSchemaName}\`.`,
|
|
15167
|
-
"",
|
|
15168
|
-
`Call \`${submitTool}\` exactly once with the payload.`,
|
|
15169
|
-
`The runtime captures the validated arguments for attempt completion.`,
|
|
15170
|
-
`Do NOT emit the output as plain assistant text. Do NOT rely on a`,
|
|
15171
|
-
`JSON-in-message fallback. If you do not call \`${submitTool}\`, the`,
|
|
15172
|
-
`attempt is recorded as failing the promised submit-output criterion`,
|
|
15173
|
-
`even if the underlying work succeeded.`,
|
|
15174
|
-
"",
|
|
15175
|
-
`Your final assistant text before that tool call may explain your work,`,
|
|
15176
|
-
`but the submit-tool call itself must be your VERY LAST action.`,
|
|
15177
|
-
"",
|
|
15178
|
-
`Task artifacts: when you produce large files, binary files, logs, reports,`,
|
|
15179
|
-
`screenshots, traces, bundles, or datasets, save them in the task workspace`,
|
|
15180
|
-
`and call \`moltnet_upload_task_artifact\` before the submit-output tool.`,
|
|
15181
|
-
`Put the returned artifact CID in the structured output where the schema`,
|
|
15182
|
-
`allows artifact metadata (for example \`artifacts[].cid\`). Do not paste`,
|
|
15183
|
-
`large bytes into structured output.`,
|
|
15184
|
-
"",
|
|
15185
|
-
`Referenced inputs: if this task depends on prior task artifacts, call`,
|
|
15186
|
-
`\`moltnet_list_task_artifacts\` for the referenced task and download the`,
|
|
15187
|
-
`specific CID you need with \`moltnet_download_task_artifact\` before judging`,
|
|
15188
|
-
`or continuing that work.`,
|
|
15189
|
-
`For a bound input artifact, omit \`attemptN\` because it has no producing`,
|
|
15190
|
-
`attempt. Pass \`attemptN\` only for an artifact from one exact task attempt.`,
|
|
15191
|
-
"",
|
|
15192
|
-
`Output shape:`,
|
|
15193
|
-
"",
|
|
15194
|
-
"```json",
|
|
15195
|
-
shapeSketch,
|
|
15196
|
-
"```"
|
|
15197
|
-
];
|
|
15198
|
-
if (extraNotes?.length) {
|
|
15199
|
-
lines.push("");
|
|
15200
|
-
for (const note of extraNotes) lines.push(note);
|
|
15201
|
-
}
|
|
15202
|
-
return lines.join("\n");
|
|
15203
|
-
}
|
|
15204
|
-
//#endregion
|
|
15205
|
-
//#region ../agent-runtime/src/prompts/proactive-memory.ts
|
|
15206
|
-
function buildProactiveMemoryWorkflowBlock() {
|
|
15207
|
-
return [
|
|
15208
|
-
"Before material work, apply the runtime instructor's proactive memory",
|
|
15209
|
-
"rules instead of waiting for a human prompt. Start with constrained",
|
|
15210
|
-
"diary context: inspect tags/list entries when task provenance or scope",
|
|
15211
|
-
"tags are known, then use `moltnet_search_entries` with `taskFilter`,",
|
|
15212
|
-
"`entryTypes`, and tags. Do not run broad unfiltered searches before",
|
|
15213
|
-
"constrained searches miss.",
|
|
15214
|
-
"",
|
|
15215
|
-
"For incident capture, follow the runtime instructor exactly: search",
|
|
15216
|
-
"for similar episodic/semantic entries first, reference close matches,",
|
|
15217
|
-
"and create a recurrence entry only when the repeat is useful signal."
|
|
15218
|
-
].join("\n");
|
|
15219
|
-
}
|
|
15220
|
-
//#endregion
|
|
15221
15213
|
//#region ../agent-runtime/src/prompts/rubric-common.ts
|
|
15222
15214
|
function renderRubricCriteriaList(rubric) {
|
|
15223
15215
|
return rubric.criteria.map((c, i) => `${i + 1}. **${c.id}** (weight ${c.weight}, scoring: \`${c.scoring}\`) — ${c.description}`).join("\n");
|
|
@@ -15297,9 +15289,7 @@ function buildAssessBriefUserPrompt(input, ctx) {
|
|
|
15297
15289
|
const scoring = [
|
|
15298
15290
|
"- `llm_score`: score 0..1 continuous. `rationale` REQUIRED (2–4 sentences).",
|
|
15299
15291
|
"- `boolean`: score exactly 0 or 1. `rationale` optional.",
|
|
15300
|
-
"- `deterministic_signature_check`: run `moltnet entry verify` on every diary entry returned by step 3 above AND `git verify-commit` on every commit. Score 1 iff ALL signatures are valid; otherwise 0. Populate `evidence.commitsVerified`, `evidence.commitsTotal`, `evidence.signatureFailures`."
|
|
15301
|
-
"",
|
|
15302
|
-
"Write a signed diary entry (tags: \"judgment\", \"assess_brief\") capturing the rationale before reporting structured output."
|
|
15292
|
+
"- `deterministic_signature_check`: run `moltnet entry verify` on every diary entry returned by step 3 above AND `git verify-commit` on every commit. Score 1 iff ALL signatures are valid; otherwise 0. Populate `evidence.commitsVerified`, `evidence.commitsTotal`, `evidence.signatureFailures`."
|
|
15303
15293
|
].join("\n");
|
|
15304
15294
|
return assembleTaskPrompt("assess_brief", [
|
|
15305
15295
|
{
|
|
@@ -15319,12 +15309,6 @@ function buildAssessBriefUserPrompt(input, ctx) {
|
|
|
15319
15309
|
header: "Querying the producer's diary entries",
|
|
15320
15310
|
body: diaryQuery
|
|
15321
15311
|
},
|
|
15322
|
-
{
|
|
15323
|
-
id: "assess_brief.proactive_memory",
|
|
15324
|
-
source: "discipline",
|
|
15325
|
-
header: "Proactive memory use",
|
|
15326
|
-
body: buildProactiveMemoryWorkflowBlock()
|
|
15327
|
-
},
|
|
15328
15312
|
{
|
|
15329
15313
|
id: "assess_brief.workspace",
|
|
15330
15314
|
source: "workspace",
|
|
@@ -15347,88 +15331,10 @@ function buildAssessBriefUserPrompt(input, ctx) {
|
|
|
15347
15331
|
source: "rubric_judge",
|
|
15348
15332
|
header: "Scoring rules",
|
|
15349
15333
|
body: scoring
|
|
15350
|
-
},
|
|
15351
|
-
{
|
|
15352
|
-
id: "assess_brief.final_output",
|
|
15353
|
-
source: "final_output",
|
|
15354
|
-
body: buildFinalOutputBlock({
|
|
15355
|
-
taskType: "assess_brief",
|
|
15356
|
-
outputSchemaName: "AssessBriefOutput",
|
|
15357
|
-
shapeSketch: [
|
|
15358
|
-
"{",
|
|
15359
|
-
" \"scores\": [",
|
|
15360
|
-
" { \"criterionId\": \"...\", \"score\": 0.0, \"rationale\": \"...\", \"evidence\": {} }",
|
|
15361
|
-
" ],",
|
|
15362
|
-
" \"composite\": <sum>,",
|
|
15363
|
-
" \"verdict\": \"<1-3 sentence overall>\",",
|
|
15364
|
-
" \"judgeModel\": \"<provider:model>\"",
|
|
15365
|
-
"}"
|
|
15366
|
-
].join("\n"),
|
|
15367
|
-
extraNotes: ["`composite` = Σ(weight_i × score_i) recomputed. The runtime rejects a mismatch."]
|
|
15368
|
-
})
|
|
15369
15334
|
}
|
|
15370
15335
|
]);
|
|
15371
15336
|
}
|
|
15372
15337
|
//#endregion
|
|
15373
|
-
//#region ../agent-runtime/src/prompts/self-verification.ts
|
|
15374
|
-
function buildSelfVerificationBlock(taskId, criteriaField = "successCriteria") {
|
|
15375
|
-
return [
|
|
15376
|
-
"## Self-verification",
|
|
15377
|
-
"",
|
|
15378
|
-
`If \`input.${criteriaField}\` is set on this task, your final output MUST`,
|
|
15379
|
-
"include a `verification` block. Treat every item in those criteria as",
|
|
15380
|
-
"part of the promise you made when you claimed the task. That includes",
|
|
15381
|
-
"the built-in submit-output gate when present. Do not call the submit",
|
|
15382
|
-
"tool until you have computed the verification payload you can honestly",
|
|
15383
|
-
"stand behind.",
|
|
15384
|
-
"",
|
|
15385
|
-
`Call \`moltnet_get_task\` with task id \`${taskId}\` and read \`input.${criteriaField}\`.`,
|
|
15386
|
-
"",
|
|
15387
|
-
`- If \`input.${criteriaField}\` is **absent**, omit \`verification\` from your`,
|
|
15388
|
-
" final output entirely.",
|
|
15389
|
-
`- If \`input.${criteriaField}\` is **present**, evaluate every applicable`,
|
|
15390
|
-
" item — `gates`, `assertions`, `rubric` criteria, `sideEffects` — against",
|
|
15391
|
-
" your produced work and emit one result per id. Be honest: a `fail` with",
|
|
15392
|
-
" a one-line reason is more useful than a false `pass`. Use `skip` (with a",
|
|
15393
|
-
" `detail`) when you genuinely could not determine a result. Compute",
|
|
15394
|
-
" `passed = results.every(r => r.status !== 'fail')`.",
|
|
15395
|
-
"- `verification` MUST be a JSON object. Never send a string, markdown",
|
|
15396
|
-
" block, null, or an empty placeholder. The submit tool expects an object",
|
|
15397
|
-
" with `inputCid`, `results`, and `passed` fields.",
|
|
15398
|
-
"",
|
|
15399
|
-
"Verification shape:",
|
|
15400
|
-
"",
|
|
15401
|
-
"```json",
|
|
15402
|
-
"{",
|
|
15403
|
-
" \"inputCid\": \"<the inputCid you saw on the task>\",",
|
|
15404
|
-
" \"results\": [",
|
|
15405
|
-
" { \"id\": \"<criterion id>\", \"kind\": \"assertion|gate|rubric|sideEffect\",",
|
|
15406
|
-
" \"status\": \"pass|fail|skip\", \"detail\": \"<optional one-liner>\" }",
|
|
15407
|
-
" ],",
|
|
15408
|
-
" \"passed\": <boolean>",
|
|
15409
|
-
"}",
|
|
15410
|
-
"```",
|
|
15411
|
-
"",
|
|
15412
|
-
"Minimal valid example:",
|
|
15413
|
-
"",
|
|
15414
|
-
"```json",
|
|
15415
|
-
"{",
|
|
15416
|
-
" \"inputCid\": \"<task inputCid>\",",
|
|
15417
|
-
" \"results\": [",
|
|
15418
|
-
" {",
|
|
15419
|
-
" \"id\": \"<criterion id>\",",
|
|
15420
|
-
" \"kind\": \"rubric\",",
|
|
15421
|
-
" \"status\": \"pass\",",
|
|
15422
|
-
" \"detail\": \"one-line reason\"",
|
|
15423
|
-
" }",
|
|
15424
|
-
" ],",
|
|
15425
|
-
" \"passed\": true",
|
|
15426
|
-
"}",
|
|
15427
|
-
"```",
|
|
15428
|
-
""
|
|
15429
|
-
].join("\n");
|
|
15430
|
-
}
|
|
15431
|
-
//#endregion
|
|
15432
15338
|
//#region ../agent-runtime/src/prompts/curate-pack.ts
|
|
15433
15339
|
/**
|
|
15434
15340
|
* Build the first user-message prompt for a `curate_pack` task.
|
|
@@ -15586,34 +15492,6 @@ function buildCuratePackUserPrompt(input, ctx) {
|
|
|
15586
15492
|
source: "static",
|
|
15587
15493
|
header: "Hard constraints",
|
|
15588
15494
|
body: hardConstraints
|
|
15589
|
-
},
|
|
15590
|
-
{
|
|
15591
|
-
id: "curate_pack.verification",
|
|
15592
|
-
source: "verification",
|
|
15593
|
-
body: buildSelfVerificationBlock(ctx.taskId)
|
|
15594
|
-
},
|
|
15595
|
-
{
|
|
15596
|
-
id: "curate_pack.final_output",
|
|
15597
|
-
source: "final_output",
|
|
15598
|
-
body: buildFinalOutputBlock({
|
|
15599
|
-
taskType: "curate_pack",
|
|
15600
|
-
outputSchemaName: "CuratePackOutput",
|
|
15601
|
-
shapeSketch: [
|
|
15602
|
-
"{",
|
|
15603
|
-
" \"packId\": \"<uuid>\",",
|
|
15604
|
-
" \"packCid\": \"<cid>\",",
|
|
15605
|
-
" \"entries\": [",
|
|
15606
|
-
" { \"entryId\": \"<uuid>\", \"rank\": 1, \"rationale\": \"<why>\" }",
|
|
15607
|
-
" ],",
|
|
15608
|
-
" \"recipeParams\": { \"recipe\": \"...\", \"prompt\": \"...\", ... },",
|
|
15609
|
-
" \"checkpoints\": [",
|
|
15610
|
-
" { \"phase\": \"recon\", \"candidateIds\": [...], \"droppedIds\": [...], \"notes\": \"...\" }",
|
|
15611
|
-
" ],",
|
|
15612
|
-
" \"summary\": \"<2-4 sentences: what you looked for, how you narrowed, what defines the final set>\",",
|
|
15613
|
-
" \"verification\": <required iff input.successCriteria; see Self-verification>",
|
|
15614
|
-
"}"
|
|
15615
|
-
].join("\n")
|
|
15616
|
-
})
|
|
15617
15495
|
}
|
|
15618
15496
|
]);
|
|
15619
15497
|
}
|
|
@@ -15650,16 +15528,13 @@ function buildFreeformUserPrompt(input, ctx) {
|
|
|
15650
15528
|
const expectedOutput = input.expectedOutput ?? "";
|
|
15651
15529
|
const constraints = input.constraints?.length ? input.constraints.map((constraint) => `- ${constraint}`).join("\n") : "";
|
|
15652
15530
|
const suggestedTaskType = input.suggestedTaskType ? [`The proposer suggested task type \`${input.suggestedTaskType}\`.`, "Use it as a hint, not as a contract."].join("\n") : "";
|
|
15653
|
-
const
|
|
15654
|
-
"
|
|
15655
|
-
"
|
|
15656
|
-
"
|
|
15657
|
-
"
|
|
15658
|
-
"
|
|
15659
|
-
"
|
|
15660
|
-
" `proposedTaskType` in the final output with a concise rationale.",
|
|
15661
|
-
"6. If you changed code on a branch, include that branch in",
|
|
15662
|
-
" `branch` so future continuations can recover git context."
|
|
15531
|
+
const outcomeHints = [
|
|
15532
|
+
"Complete the brief using the supplied task facts, context, and effective",
|
|
15533
|
+
"runtime capabilities.",
|
|
15534
|
+
"If the request reveals a recurring task shape, populate the",
|
|
15535
|
+
"`proposedTaskType` field exposed by the registered submit-output tool.",
|
|
15536
|
+
"If you changed code on a branch, populate its `branch` field so a",
|
|
15537
|
+
"continuation can recover that git context."
|
|
15663
15538
|
].join("\n");
|
|
15664
15539
|
const sections = [
|
|
15665
15540
|
{
|
|
@@ -15692,43 +15567,14 @@ function buildFreeformUserPrompt(input, ctx) {
|
|
|
15692
15567
|
body: suggestedTaskType
|
|
15693
15568
|
},
|
|
15694
15569
|
{
|
|
15695
|
-
id: "freeform.
|
|
15570
|
+
id: "freeform.outcome_hints",
|
|
15696
15571
|
source: "static",
|
|
15697
|
-
header: "
|
|
15698
|
-
body:
|
|
15699
|
-
},
|
|
15700
|
-
{
|
|
15701
|
-
id: "freeform.proactive_memory",
|
|
15702
|
-
source: "discipline",
|
|
15703
|
-
header: "Proactive memory use",
|
|
15704
|
-
body: buildProactiveMemoryWorkflowBlock()
|
|
15705
|
-
},
|
|
15706
|
-
{
|
|
15707
|
-
id: "freeform.verification",
|
|
15708
|
-
source: "verification",
|
|
15709
|
-
body: buildSelfVerificationBlock(ctx.taskId)
|
|
15710
|
-
},
|
|
15711
|
-
{
|
|
15712
|
-
id: "freeform.final_output",
|
|
15713
|
-
source: "final_output",
|
|
15714
|
-
body: buildFinalOutputBlock({
|
|
15715
|
-
taskType: "freeform",
|
|
15716
|
-
outputSchemaName: "FreeformOutput",
|
|
15717
|
-
shapeSketch: [
|
|
15718
|
-
"{",
|
|
15719
|
-
" \"summary\": \"<2-5 sentence result>\",",
|
|
15720
|
-
" \"branch\": \"<branch name when code changed; omit for prose-only work>\",",
|
|
15721
|
-
" \"artifacts\": [{ \"kind\": \"...\", \"title\": \"...\", \"description\": \"...\", \"body\": \"<inline content up to 64 KiB; preferred for textual output so it persists with the task>\", \"url\": \"...\", \"path\": \"<worktree-ephemeral; not persisted after completion>\" }],",
|
|
15722
|
-
" \"proposedTaskType\": { \"name\": \"...\", \"rationale\": \"...\", \"inputShape\": {}, \"outputShape\": {} },",
|
|
15723
|
-
" \"diaryEntryIds\": [\"...\"],",
|
|
15724
|
-
" \"verification\": <required iff input.successCriteria; see Self-verification>",
|
|
15725
|
-
"}"
|
|
15726
|
-
].join("\n")
|
|
15727
|
-
})
|
|
15572
|
+
header: "Outcome hints",
|
|
15573
|
+
body: outcomeHints
|
|
15728
15574
|
}
|
|
15729
15575
|
];
|
|
15730
15576
|
const priorContextBody = buildPriorContextSection(ctx.priorContext);
|
|
15731
|
-
if (priorContextBody) sections.
|
|
15577
|
+
if (priorContextBody) sections.push({
|
|
15732
15578
|
id: "freeform.prior_context",
|
|
15733
15579
|
source: "task_input",
|
|
15734
15580
|
body: priorContextBody
|
|
@@ -15745,22 +15591,18 @@ function buildFreeformUserPrompt(input, ctx) {
|
|
|
15745
15591
|
* is told to inspect them itself.
|
|
15746
15592
|
*/
|
|
15747
15593
|
function buildFulfillBriefUserPrompt(input, ctx) {
|
|
15748
|
-
const { brief, seedFiles
|
|
15594
|
+
const { brief, seedFiles } = input;
|
|
15749
15595
|
const header = [
|
|
15750
15596
|
"# Fulfill Brief Agent",
|
|
15751
15597
|
"",
|
|
15752
15598
|
"You are a software engineering agent working in a sandboxed environment.",
|
|
15753
15599
|
"Use the current working directory as the task workspace.",
|
|
15754
|
-
"The MoltNet runtime instructor (above, in this system prompt) defines the",
|
|
15755
|
-
"invariants for this task: identity, gh authentication, diary discipline,",
|
|
15756
|
-
"and the accountable-commit shape. Follow it for every commit.",
|
|
15757
15600
|
"",
|
|
15758
15601
|
"## Task: Fulfill brief",
|
|
15759
15602
|
"",
|
|
15760
15603
|
`Task id: \`${ctx.taskId}\``
|
|
15761
15604
|
].join("\n");
|
|
15762
15605
|
const seedFilesBody = seedFiles?.length ? ["Start by reading these files to ground yourself:", ...seedFiles.map((f) => `- \`${f}\``)].join("\n") : "";
|
|
15763
|
-
const branchSlug = ctx.correlationId ? `moltnet/${ctx.correlationId}/` : scopeHint ? `feat/${scopeHint}-` : "feat/";
|
|
15764
15606
|
const correlation = ctx.correlationId ? [
|
|
15765
15607
|
`This task carries correlationId \`${ctx.correlationId}\`. You MUST:`,
|
|
15766
15608
|
"",
|
|
@@ -15777,23 +15619,6 @@ function buildFulfillBriefUserPrompt(input, ctx) {
|
|
|
15777
15619
|
"for this task. Do not repurpose or switch the primary checkout.",
|
|
15778
15620
|
ctx.workspace.branch ? `The current branch is \`${ctx.workspace.branch}\`. Stay on this branch unless the runtime instructor explicitly tells you otherwise.` : "Stay on the branch that was pre-provisioned for this task."
|
|
15779
15621
|
].join("\n") : "";
|
|
15780
|
-
const workflow = [
|
|
15781
|
-
ctx.workspace?.mode === "dedicated_worktree" ? `1. Use the already-provisioned dedicated worktree branch${ctx.workspace.branch ? ` (\`${ctx.workspace.branch}\`)` : ""}; do not create or switch the primary checkout.` : `1. Create a feature branch (starting prefix suggestion: \`${branchSlug}<short-slug>\`).`,
|
|
15782
|
-
"2. Search MoltNet diary memory for prior decisions, incidents, and",
|
|
15783
|
-
" recurring traps relevant to the brief before changing code.",
|
|
15784
|
-
"3. Understand the problem — read relevant code; do not speculate.",
|
|
15785
|
-
"4. Implement the change. Keep commits small and coherent.",
|
|
15786
|
-
"5. Add tests if applicable.",
|
|
15787
|
-
"6. For every commit, create a signed diary entry first via",
|
|
15788
|
-
" `moltnet_create_entry` and embed its id in the commit trailer",
|
|
15789
|
-
" `MoltNet-Diary: <id>` (per the runtime instructor).",
|
|
15790
|
-
"7. Push the branch and open a PR — run `git push` and `gh pr create`",
|
|
15791
|
-
" IN the VM with your normal `bash` tool (use the",
|
|
15792
|
-
" `GH_TOKEN=$(moltnet github token …) gh …` form from the runtime",
|
|
15793
|
-
" instructor for writes; read-only `gh` commands may run bare). Do NOT",
|
|
15794
|
-
" use `moltnet_host_exec` for this; it needs human",
|
|
15795
|
-
" approval that is unavailable in a headless run."
|
|
15796
|
-
].join("\n");
|
|
15797
15622
|
return assembleTaskPrompt("fulfill_brief", [
|
|
15798
15623
|
{
|
|
15799
15624
|
id: "fulfill_brief.header",
|
|
@@ -15823,41 +15648,6 @@ function buildFulfillBriefUserPrompt(input, ctx) {
|
|
|
15823
15648
|
source: "workspace",
|
|
15824
15649
|
header: "Workspace",
|
|
15825
15650
|
body: workspace
|
|
15826
|
-
},
|
|
15827
|
-
{
|
|
15828
|
-
id: "fulfill_brief.workflow",
|
|
15829
|
-
source: "static",
|
|
15830
|
-
header: "Workflow",
|
|
15831
|
-
body: workflow
|
|
15832
|
-
},
|
|
15833
|
-
{
|
|
15834
|
-
id: "fulfill_brief.proactive_memory",
|
|
15835
|
-
source: "discipline",
|
|
15836
|
-
header: "Proactive memory use",
|
|
15837
|
-
body: buildProactiveMemoryWorkflowBlock()
|
|
15838
|
-
},
|
|
15839
|
-
{
|
|
15840
|
-
id: "fulfill_brief.verification",
|
|
15841
|
-
source: "verification",
|
|
15842
|
-
body: buildSelfVerificationBlock(ctx.taskId)
|
|
15843
|
-
},
|
|
15844
|
-
{
|
|
15845
|
-
id: "fulfill_brief.final_output",
|
|
15846
|
-
source: "final_output",
|
|
15847
|
-
body: buildFinalOutputBlock({
|
|
15848
|
-
taskType: "fulfill_brief",
|
|
15849
|
-
outputSchemaName: "FulfillBriefOutput",
|
|
15850
|
-
shapeSketch: [
|
|
15851
|
-
"{",
|
|
15852
|
-
" \"branch\": \"<branch-name>\",",
|
|
15853
|
-
" \"commits\": [{ \"sha\": \"...\", \"message\": \"...\", \"diaryEntryId\": \"...\" }],",
|
|
15854
|
-
" \"pullRequestUrl\": \"<url-or-null>\",",
|
|
15855
|
-
" \"diaryEntryIds\": [\"...\"],",
|
|
15856
|
-
" \"summary\": \"<1-3 sentence recap>\",",
|
|
15857
|
-
" \"verification\": <required iff input.successCriteria; see Self-verification>",
|
|
15858
|
-
"}"
|
|
15859
|
-
].join("\n")
|
|
15860
|
-
})
|
|
15861
15651
|
}
|
|
15862
15652
|
]);
|
|
15863
15653
|
}
|
|
@@ -15934,26 +15724,6 @@ function buildJudgeEvalAttemptUserPrompt(input, ctx) {
|
|
|
15934
15724
|
source: "rubric_judge",
|
|
15935
15725
|
header: "Composite arithmetic",
|
|
15936
15726
|
body: composite
|
|
15937
|
-
},
|
|
15938
|
-
{
|
|
15939
|
-
id: "judge_eval_attempt.final_output",
|
|
15940
|
-
source: "final_output",
|
|
15941
|
-
body: buildFinalOutputBlock({
|
|
15942
|
-
taskType: "judge_eval_attempt",
|
|
15943
|
-
outputSchemaName: "JudgeEvalAttemptOutput",
|
|
15944
|
-
shapeSketch: [
|
|
15945
|
-
"{",
|
|
15946
|
-
` "targetTaskId": "${input.targetTaskId}",`,
|
|
15947
|
-
` "targetAttemptN": ${input.targetAttemptN},`,
|
|
15948
|
-
" \"variantLabel\": \"<from producer input>\",",
|
|
15949
|
-
" \"scores\": [ { \"criterionId\": \"...\", \"score\": 0..1, \"rationale\": \"...\", \"assertions\": [...]?, \"evidence\": { \"text\": \"...\" } } ],",
|
|
15950
|
-
" \"composite\": <Σ(weight × score), 0..1>,",
|
|
15951
|
-
" \"verdict\": \"<1-3 sentences>\",",
|
|
15952
|
-
" \"judgeModel\": \"<id>\", // optional",
|
|
15953
|
-
" \"traceparent\": \"<from claim>\"",
|
|
15954
|
-
"}"
|
|
15955
|
-
].join("\n")
|
|
15956
|
-
})
|
|
15957
15727
|
}
|
|
15958
15728
|
]);
|
|
15959
15729
|
}
|
|
@@ -16035,9 +15805,7 @@ function buildJudgePackUserPrompt(input, ctx) {
|
|
|
16035
15805
|
"- Do NOT call `moltnet_pack_create` or `moltnet_pack_render`.",
|
|
16036
15806
|
"- Do NOT fetch the curator's or renderer's task output directly — they",
|
|
16037
15807
|
" may leak guidance that biases judgment.",
|
|
16038
|
-
"- Keep the session focused on scoring; no speculative exploration."
|
|
16039
|
-
"",
|
|
16040
|
-
`Write a signed diary entry (tags: \`judgment\`, \`judge_pack\`, \`rubric:${rubric.rubricId}\`) capturing the rationale before reporting structured output.`
|
|
15808
|
+
"- Keep the session focused on scoring; no speculative exploration."
|
|
16041
15809
|
].join("\n");
|
|
16042
15810
|
return assembleTaskPrompt("judge_pack", [
|
|
16043
15811
|
{
|
|
@@ -16079,37 +15847,6 @@ function buildJudgePackUserPrompt(input, ctx) {
|
|
|
16079
15847
|
source: "static",
|
|
16080
15848
|
header: "Constraints",
|
|
16081
15849
|
body: constraints
|
|
16082
|
-
},
|
|
16083
|
-
{
|
|
16084
|
-
id: "judge_pack.final_output",
|
|
16085
|
-
source: "final_output",
|
|
16086
|
-
body: buildFinalOutputBlock({
|
|
16087
|
-
taskType: "judge_pack",
|
|
16088
|
-
outputSchemaName: "JudgePackOutput",
|
|
16089
|
-
shapeSketch: [
|
|
16090
|
-
"{",
|
|
16091
|
-
" \"scores\": [",
|
|
16092
|
-
" { \"criterionId\": \"...\", \"score\": 0.0, \"rationale\": \"...\", \"evidence\": {} },",
|
|
16093
|
-
" {",
|
|
16094
|
-
" \"criterionId\": \"<llm_checklist criterion>\",",
|
|
16095
|
-
" \"score\": 0, // 1 iff every assertion passed",
|
|
16096
|
-
" \"assertions\": [",
|
|
16097
|
-
" { \"id\": \"claim-1\", \"text\": \"...\", \"passed\": false, \"evidence\": \"...\" }",
|
|
16098
|
-
" ]",
|
|
16099
|
-
" }",
|
|
16100
|
-
" ],",
|
|
16101
|
-
" \"composite\": <sum-of-weighted-scores>,",
|
|
16102
|
-
" \"verdict\": \"<1-3 sentence overall>\",",
|
|
16103
|
-
" \"judgeModel\": \"<provider:model>\",",
|
|
16104
|
-
" \"rendererBinaryCid\": \"<cid-string-only-if-available>\"",
|
|
16105
|
-
"}"
|
|
16106
|
-
].join("\n"),
|
|
16107
|
-
extraNotes: [
|
|
16108
|
-
"Omit `rendererBinaryCid` entirely when no binary CID is exposed by",
|
|
16109
|
-
"`moltnet_rendered_pack_get`. Do NOT emit `null` — the field is",
|
|
16110
|
-
"optional and absence is the correct representation when unavailable."
|
|
16111
|
-
]
|
|
16112
|
-
})
|
|
16113
15850
|
}
|
|
16114
15851
|
]);
|
|
16115
15852
|
}
|
|
@@ -16152,20 +15889,6 @@ function buildPrReviewUserPrompt(input, ctx) {
|
|
|
16152
15889
|
"(for example publishing the judgment somewhere), perform that action as",
|
|
16153
15890
|
"part of the task before reporting structured output."
|
|
16154
15891
|
].join("\n");
|
|
16155
|
-
const workflow = [
|
|
16156
|
-
"1. Read the subject summary, resources, inspection hints, and any",
|
|
16157
|
-
" task-specific instructions before scoring.",
|
|
16158
|
-
"2. Search MoltNet diary memory for prior decisions, incidents, and",
|
|
16159
|
-
" recurring review traps relevant to the subject.",
|
|
16160
|
-
"3. Inspect the target artefact directly using the tools and resources the",
|
|
16161
|
-
" task makes available.",
|
|
16162
|
-
"4. If you are in a dedicated disposable worktree and need the review target",
|
|
16163
|
-
" checked out locally, do that work inside this disposable workspace only.",
|
|
16164
|
-
"5. Apply the rubric strictly. This task is about complexity and",
|
|
16165
|
-
" reviewability, not correctness or feature desirability.",
|
|
16166
|
-
"6. Perform any required outward action before emitting the final",
|
|
16167
|
-
" structured output."
|
|
16168
|
-
].join("\n");
|
|
16169
15892
|
const taskPromptSection = input.taskPrompt ?? "";
|
|
16170
15893
|
const preamble = renderRubricPreambleSection(rubric) ?? "";
|
|
16171
15894
|
const criteria = renderRubricCriteriaList(rubric);
|
|
@@ -16174,9 +15897,7 @@ function buildPrReviewUserPrompt(input, ctx) {
|
|
|
16174
15897
|
"- Score `1` when the subject clearly clears the criterion.",
|
|
16175
15898
|
"- Score `0` when it does not, or when the evidence is ambiguous.",
|
|
16176
15899
|
"- `rationale` is REQUIRED for every score. Keep it concrete and audit-friendly.",
|
|
16177
|
-
"- Compute `composite = Σ(weight_i × score_i)` exactly; the runtime rejects mismatches."
|
|
16178
|
-
"",
|
|
16179
|
-
"Write a signed diary entry (tags: `judgment`, `pr_review`) capturing the rationale before reporting structured output."
|
|
15900
|
+
"- Compute `composite = Σ(weight_i × score_i)` exactly; the runtime rejects mismatches."
|
|
16180
15901
|
].join("\n");
|
|
16181
15902
|
return assembleTaskPrompt("pr_review", [
|
|
16182
15903
|
{
|
|
@@ -16214,18 +15935,6 @@ function buildPrReviewUserPrompt(input, ctx) {
|
|
|
16214
15935
|
header: "Execution contract",
|
|
16215
15936
|
body: executionContract
|
|
16216
15937
|
},
|
|
16217
|
-
{
|
|
16218
|
-
id: "pr_review.workflow",
|
|
16219
|
-
source: "static",
|
|
16220
|
-
header: "Review workflow",
|
|
16221
|
-
body: workflow
|
|
16222
|
-
},
|
|
16223
|
-
{
|
|
16224
|
-
id: "pr_review.proactive_memory",
|
|
16225
|
-
source: "discipline",
|
|
16226
|
-
header: "Proactive memory use",
|
|
16227
|
-
body: buildProactiveMemoryWorkflowBlock()
|
|
16228
|
-
},
|
|
16229
15938
|
{
|
|
16230
15939
|
id: "pr_review.task_prompt",
|
|
16231
15940
|
source: "task_input",
|
|
@@ -16248,24 +15957,6 @@ function buildPrReviewUserPrompt(input, ctx) {
|
|
|
16248
15957
|
source: "rubric_judge",
|
|
16249
15958
|
header: "Scoring rules",
|
|
16250
15959
|
body: scoring
|
|
16251
|
-
},
|
|
16252
|
-
{
|
|
16253
|
-
id: "pr_review.final_output",
|
|
16254
|
-
source: "final_output",
|
|
16255
|
-
body: buildFinalOutputBlock({
|
|
16256
|
-
taskType: "pr_review",
|
|
16257
|
-
outputSchemaName: "PrReviewOutput",
|
|
16258
|
-
shapeSketch: [
|
|
16259
|
-
"{",
|
|
16260
|
-
" \"scores\": [",
|
|
16261
|
-
" { \"criterionId\": \"...\", \"score\": 0, \"rationale\": \"...\" }",
|
|
16262
|
-
" ],",
|
|
16263
|
-
" \"composite\": <sum-of-weighted-binary-scores>,",
|
|
16264
|
-
" \"verdict\": \"<1-3 sentence overall>\"",
|
|
16265
|
-
"}"
|
|
16266
|
-
].join("\n"),
|
|
16267
|
-
extraNotes: ["`scores` MUST stay in the same order as the rubric criteria.", "`score` MUST be exactly `0` or `1` for every criterion."]
|
|
16268
|
-
})
|
|
16269
15960
|
}
|
|
16270
15961
|
]);
|
|
16271
15962
|
}
|
|
@@ -16358,30 +16049,6 @@ function buildRenderPackUserPrompt(input, ctx) {
|
|
|
16358
16049
|
source: "static",
|
|
16359
16050
|
header: "Fidelity Discipline",
|
|
16360
16051
|
body: fidelity
|
|
16361
|
-
},
|
|
16362
|
-
{
|
|
16363
|
-
id: "render_pack.verification",
|
|
16364
|
-
source: "verification",
|
|
16365
|
-
body: buildSelfVerificationBlock(ctx.taskId)
|
|
16366
|
-
},
|
|
16367
|
-
{
|
|
16368
|
-
id: "render_pack.final_output",
|
|
16369
|
-
source: "final_output",
|
|
16370
|
-
body: buildFinalOutputBlock({
|
|
16371
|
-
taskType: "render_pack",
|
|
16372
|
-
outputSchemaName: "RenderPackOutput",
|
|
16373
|
-
shapeSketch: [
|
|
16374
|
-
"{",
|
|
16375
|
-
" \"renderedPackId\": \"<uuid-or-null>\",",
|
|
16376
|
-
" \"renderedCid\": \"<cid>\",",
|
|
16377
|
-
" \"renderMethod\": \"<label>\",",
|
|
16378
|
-
" \"byteSize\": <int>,",
|
|
16379
|
-
" \"entriesRendered\": <int>,",
|
|
16380
|
-
" \"summary\": \"<1-3 sentence recap>\",",
|
|
16381
|
-
" \"verification\": <required iff input.successCriteria; see Self-verification>",
|
|
16382
|
-
"}"
|
|
16383
|
-
].join("\n")
|
|
16384
|
-
})
|
|
16385
16052
|
}
|
|
16386
16053
|
]);
|
|
16387
16054
|
}
|
|
@@ -16416,7 +16083,7 @@ function buildRenderPackUserPrompt(input, ctx) {
|
|
|
16416
16083
|
* field. Quoting the constraint back is not following the task.
|
|
16417
16084
|
*/
|
|
16418
16085
|
function buildRunEvalUserPrompt(input, ctx) {
|
|
16419
|
-
const { scenario, variantLabel
|
|
16086
|
+
const { scenario, variantLabel } = input;
|
|
16420
16087
|
const effectiveRuntimeContext = ctx.effectiveRuntimeContext ?? input.context;
|
|
16421
16088
|
const hasContext = effectiveRuntimeContext.length > 0;
|
|
16422
16089
|
const hasInlineContext = effectiveRuntimeContext.some((entry) => entry.binding === "context_inline");
|
|
@@ -16437,27 +16104,6 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
16437
16104
|
"rules, those rules override your generic instincts."
|
|
16438
16105
|
].join("\n") : "";
|
|
16439
16106
|
const inputFiles = scenario.inputFiles?.length ? scenario.inputFiles.map((f) => `- \`${f}\``).join("\n") : "";
|
|
16440
|
-
const verification = successCriteria ? buildSelfVerificationBlock(ctx.taskId) : "";
|
|
16441
|
-
const finalOutput = buildFinalOutputBlock({
|
|
16442
|
-
taskType: "run_eval",
|
|
16443
|
-
outputSchemaName: "RunEvalOutput",
|
|
16444
|
-
shapeSketch: [
|
|
16445
|
-
"{",
|
|
16446
|
-
" \"response\": \"<your free-form answer>\",",
|
|
16447
|
-
" \"artifacts\": [{ \"path\": \"...\", \"cid\": \"...\" }], // optional",
|
|
16448
|
-
" \"totalTokens\": <int>,",
|
|
16449
|
-
" \"durationMs\": <int>,",
|
|
16450
|
-
" \"traceparent\": \"<from claim>\",",
|
|
16451
|
-
" \"verification\": {",
|
|
16452
|
-
" \"inputCid\": \"<task inputCid>\",",
|
|
16453
|
-
" \"results\": [",
|
|
16454
|
-
" { \"id\": \"<criterion id>\", \"kind\": \"rubric\", \"status\": \"pass|fail|skip\", \"detail\": \"<optional one-liner>\" }",
|
|
16455
|
-
" ],",
|
|
16456
|
-
" \"passed\": <boolean>",
|
|
16457
|
-
" } // required iff input.successCriteria; must be an object, never a string",
|
|
16458
|
-
"}"
|
|
16459
|
-
].join("\n")
|
|
16460
|
-
});
|
|
16461
16107
|
return assembleTaskPrompt("run_eval", [
|
|
16462
16108
|
{
|
|
16463
16109
|
id: "run_eval.header",
|
|
@@ -16481,16 +16127,6 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
16481
16127
|
source: "task_input",
|
|
16482
16128
|
header: "Input files",
|
|
16483
16129
|
body: inputFiles
|
|
16484
|
-
},
|
|
16485
|
-
{
|
|
16486
|
-
id: "run_eval.verification",
|
|
16487
|
-
source: "verification",
|
|
16488
|
-
body: verification
|
|
16489
|
-
},
|
|
16490
|
-
{
|
|
16491
|
-
id: "run_eval.final_output",
|
|
16492
|
-
source: "final_output",
|
|
16493
|
-
body: finalOutput
|
|
16494
16130
|
}
|
|
16495
16131
|
]);
|
|
16496
16132
|
}
|
|
@@ -16505,14 +16141,17 @@ function submissionAcceptsVerification(taskType) {
|
|
|
16505
16141
|
/**
|
|
16506
16142
|
* Add only the dynamic contract facts that a producer cannot infer from its
|
|
16507
16143
|
* task-specific prompt: the declared success criteria and the immutable input
|
|
16508
|
-
* CID its verification must cite.
|
|
16509
|
-
* the
|
|
16144
|
+
* provenance CID its verification must cite. The CID is not a task artifact,
|
|
16145
|
+
* so the prompt states that it cannot be downloaded. This is deliberately not
|
|
16146
|
+
* a workflow block; the submit tool owns the output shape and profiles own
|
|
16147
|
+
* optional behavior.
|
|
16510
16148
|
*/
|
|
16511
16149
|
function appendTaskContractFacts(prompt, task) {
|
|
16512
16150
|
if (!hasSuccessCriteria(task.input) || !submissionAcceptsVerification(task.taskType)) return prompt;
|
|
16513
16151
|
const criteriaJson = JSON.stringify(task.input.successCriteria, null, 2);
|
|
16514
16152
|
const body = [
|
|
16515
|
-
|
|
16153
|
+
"Task input verification CID (provenance only; this is not a task",
|
|
16154
|
+
`artifact and cannot be downloaded): \`${task.inputCid}\``,
|
|
16516
16155
|
"",
|
|
16517
16156
|
"These typed criteria are task facts. Assess the completed work against",
|
|
16518
16157
|
"them before calling the submit-output tool. Its `verification` payload",
|
|
@@ -31084,6 +30723,12 @@ var ShellCommandAnalyzer = class ShellCommandAnalyzer {
|
|
|
31084
30723
|
}
|
|
31085
30724
|
};
|
|
31086
30725
|
//#endregion
|
|
30726
|
+
//#region src/config.ts
|
|
30727
|
+
/** Resolve Pi's host-side auth/config directory from process configuration. */
|
|
30728
|
+
function resolvePiCodingAgentDir() {
|
|
30729
|
+
return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
|
|
30730
|
+
}
|
|
30731
|
+
//#endregion
|
|
31087
30732
|
//#region src/snapshot.ts
|
|
31088
30733
|
/**
|
|
31089
30734
|
* Snapshot builder with auto-build and caching.
|
|
@@ -31464,15 +31109,19 @@ function filterModelVisibleTools(tools, policy) {
|
|
|
31464
31109
|
}
|
|
31465
31110
|
function enabledPiToolNames(input) {
|
|
31466
31111
|
if (!input.policy || input.policy.enforcement !== "enforce") return void 0;
|
|
31112
|
+
return modelVisiblePiToolNames(input);
|
|
31113
|
+
}
|
|
31114
|
+
/** Exact tool names the selected runtime and effective policy expose. */
|
|
31115
|
+
function modelVisiblePiToolNames(input) {
|
|
31467
31116
|
return [...new Set([...input.tools.map((tool) => tool.name), ...(input.extensions ?? []).flatMap((extension) => extension.declaredTools.filter((name) => isToolVisible(name, input.policy)))])].sort();
|
|
31468
31117
|
}
|
|
31469
31118
|
function isToolVisible(name, policy) {
|
|
31470
31119
|
if (!policy || policy.enforcement !== "enforce") return true;
|
|
31471
|
-
if (name === "bash") return
|
|
31120
|
+
if (name === "bash") return policy.allowedShellCommands === void 0 || policy.allowedShellCommands.length > 0;
|
|
31472
31121
|
return policy.allowedTools.has(name);
|
|
31473
31122
|
}
|
|
31474
31123
|
function isKernelTool(name) {
|
|
31475
|
-
return name.startsWith("submit_");
|
|
31124
|
+
return name.startsWith("submit_") || name === "subagent";
|
|
31476
31125
|
}
|
|
31477
31126
|
function wrapExtensionFactory(factory, contribution, policy) {
|
|
31478
31127
|
return (pi) => {
|
|
@@ -31490,7 +31139,7 @@ function wrapExtensionFactory(factory, contribution, policy) {
|
|
|
31490
31139
|
};
|
|
31491
31140
|
}
|
|
31492
31141
|
function claimToolName(names, name, owner) {
|
|
31493
|
-
if (isKernelTool(name)
|
|
31142
|
+
if (isKernelTool(name)) throw new Error(`Pi tool name "${name}" is reserved by the runtime kernel`);
|
|
31494
31143
|
const previous = names.get(name);
|
|
31495
31144
|
if (previous) throw new Error(`Pi tool name "${name}" is declared by ${previous} and ${owner}`);
|
|
31496
31145
|
names.set(name, owner);
|
|
@@ -31584,6 +31233,17 @@ async function delay(ms, signal, label) {
|
|
|
31584
31233
|
* investigation and the alternatives we rejected.
|
|
31585
31234
|
*/
|
|
31586
31235
|
var GUEST_TASK_CONTEXT_MOUNT = "/moltnet-task-context";
|
|
31236
|
+
function resolveVfsShadowConfig(config) {
|
|
31237
|
+
const patterns = config?.vfs?.shadow ?? [];
|
|
31238
|
+
if (patterns.length === 0) return {
|
|
31239
|
+
mode: "none",
|
|
31240
|
+
patterns: []
|
|
31241
|
+
};
|
|
31242
|
+
return {
|
|
31243
|
+
mode: config?.vfs?.shadowMode ?? "tmpfs",
|
|
31244
|
+
patterns
|
|
31245
|
+
};
|
|
31246
|
+
}
|
|
31587
31247
|
function shouldRunResumeCommand(entry, ctx) {
|
|
31588
31248
|
if (typeof entry === "string") return true;
|
|
31589
31249
|
const workspaceModes = entry.when?.workspaceMode;
|
|
@@ -31656,7 +31316,7 @@ function resolveVmAgentDir(config) {
|
|
|
31656
31316
|
function loadCredentials(agentDir) {
|
|
31657
31317
|
const moltnetJson = readFileSync(path.join(agentDir, "moltnet.json"), "utf8");
|
|
31658
31318
|
const agentEnvRaw = readFileSync(path.join(agentDir, "env"), "utf8");
|
|
31659
|
-
const piAgentDir =
|
|
31319
|
+
const piAgentDir = resolvePiCodingAgentDir();
|
|
31660
31320
|
const piAuthPath = path.join(piAgentDir, "auth.json");
|
|
31661
31321
|
const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
|
|
31662
31322
|
const gitconfigPath = path.join(agentDir, "gitconfig");
|
|
@@ -31811,7 +31471,7 @@ async function resumeVm(config) {
|
|
|
31811
31471
|
else vmAgentEnv[k] = v;
|
|
31812
31472
|
}
|
|
31813
31473
|
vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
|
|
31814
|
-
const vfsConfig = config.sandboxConfig
|
|
31474
|
+
const vfsConfig = resolveVfsShadowConfig(config.sandboxConfig);
|
|
31815
31475
|
let workspaceProvider = new RealFSProvider(config.mountPath);
|
|
31816
31476
|
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
31817
31477
|
shouldShadow: ({ path: shadowPath }) => shouldShadowNodeModulesPath(shadowPath),
|
|
@@ -31819,11 +31479,11 @@ async function resumeVm(config) {
|
|
|
31819
31479
|
tmpfs: new AutoParentMemoryProvider(),
|
|
31820
31480
|
writeMode: "tmpfs"
|
|
31821
31481
|
});
|
|
31822
|
-
if (vfsConfig
|
|
31823
|
-
const predicate = createShadowPathPredicate(vfsConfig.
|
|
31482
|
+
if (vfsConfig.mode !== "none") {
|
|
31483
|
+
const predicate = createShadowPathPredicate(vfsConfig.patterns);
|
|
31824
31484
|
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
31825
31485
|
shouldShadow: predicate,
|
|
31826
|
-
writeMode: vfsConfig.
|
|
31486
|
+
writeMode: vfsConfig.mode
|
|
31827
31487
|
});
|
|
31828
31488
|
}
|
|
31829
31489
|
const forwardedEnv = {};
|
|
@@ -32283,7 +31943,7 @@ async function executeGondolinGrep(vm, localCwd, guestWorkspace, params, signal)
|
|
|
32283
31943
|
const onAbort = () => ac.abort();
|
|
32284
31944
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
32285
31945
|
try {
|
|
32286
|
-
const proc = vm.exec(["
|
|
31946
|
+
const proc = vm.exec(["rg", ...args], {
|
|
32287
31947
|
signal: ac.signal,
|
|
32288
31948
|
stdout: "pipe",
|
|
32289
31949
|
stderr: "pipe"
|
|
@@ -32436,6 +32096,7 @@ function createGondolinBashOps(vm, localCwd, guestWorkspace) {
|
|
|
32436
32096
|
*/
|
|
32437
32097
|
function decideToolCall(input) {
|
|
32438
32098
|
if (input.enforcement === "off") return { allow: true };
|
|
32099
|
+
if (input.toolName.startsWith("submit_") || input.toolName === "subagent") return { allow: true };
|
|
32439
32100
|
const resolved = resolveNames(input);
|
|
32440
32101
|
if (resolved.kind === "unresolvable") return fenced(input.enforcement, "shell command could not be statically authorized", "unresolvable shell command (watch)");
|
|
32441
32102
|
const arbitraryCode = [...new Set(resolved.tools.filter((tool) => tool.risk === "arbitrary-code").map((tool) => tool.name))];
|
|
@@ -32687,6 +32348,105 @@ function createToolPolicyExtension(deps) {
|
|
|
32687
32348
|
};
|
|
32688
32349
|
}
|
|
32689
32350
|
//#endregion
|
|
32351
|
+
//#region src/runtime/capability-discovery.ts
|
|
32352
|
+
var GuestExecutableProbeError = class extends Error {
|
|
32353
|
+
code;
|
|
32354
|
+
constructor(code, message, options) {
|
|
32355
|
+
super(message, options);
|
|
32356
|
+
this.name = "GuestExecutableProbeError";
|
|
32357
|
+
this.code = code;
|
|
32358
|
+
}
|
|
32359
|
+
};
|
|
32360
|
+
var DEFAULT_PROBE_TIMEOUT_MS = 5e3;
|
|
32361
|
+
var MAX_STDERR_DETAIL_LENGTH = 1e3;
|
|
32362
|
+
/**
|
|
32363
|
+
* Verify only executables relevant to the resolved session policy.
|
|
32364
|
+
* Candidate names are positional arguments, never interpolated shell source.
|
|
32365
|
+
*/
|
|
32366
|
+
async function discoverGuestExecutables(vm, candidates, options = {}) {
|
|
32367
|
+
const unique = [...new Set(candidates)].sort();
|
|
32368
|
+
if (unique.length === 0) return {
|
|
32369
|
+
available: [],
|
|
32370
|
+
unavailable: []
|
|
32371
|
+
};
|
|
32372
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
32373
|
+
const controller = new AbortController();
|
|
32374
|
+
let timedOut = false;
|
|
32375
|
+
const timeout = setTimeout(() => {
|
|
32376
|
+
timedOut = true;
|
|
32377
|
+
controller.abort();
|
|
32378
|
+
}, timeoutMs);
|
|
32379
|
+
const abortFromCaller = () => controller.abort(options.signal?.reason);
|
|
32380
|
+
options.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
32381
|
+
if (options.signal?.aborted) abortFromCaller();
|
|
32382
|
+
let result;
|
|
32383
|
+
try {
|
|
32384
|
+
result = await vm.exec([
|
|
32385
|
+
"/bin/sh",
|
|
32386
|
+
"-lc",
|
|
32387
|
+
[
|
|
32388
|
+
"index=0",
|
|
32389
|
+
"for executable in \"$@\"; do",
|
|
32390
|
+
" if command -v \"$executable\" >/dev/null 2>&1; then",
|
|
32391
|
+
" printf \"%s\\n\" \"$index\"",
|
|
32392
|
+
" fi",
|
|
32393
|
+
" index=$((index + 1))",
|
|
32394
|
+
"done"
|
|
32395
|
+
].join("\n"),
|
|
32396
|
+
"moltnet-capability-probe",
|
|
32397
|
+
...unique
|
|
32398
|
+
], { signal: controller.signal });
|
|
32399
|
+
} catch (error) {
|
|
32400
|
+
if (options.signal?.aborted) throw error;
|
|
32401
|
+
if (timedOut) throw new GuestExecutableProbeError("capability_probe_timeout", `Guest executable capability probe timed out after ${timeoutMs}ms`, { cause: error });
|
|
32402
|
+
throw error;
|
|
32403
|
+
} finally {
|
|
32404
|
+
clearTimeout(timeout);
|
|
32405
|
+
options.signal?.removeEventListener("abort", abortFromCaller);
|
|
32406
|
+
}
|
|
32407
|
+
if (result.exitCode !== 0) {
|
|
32408
|
+
const stderr = result.stderr.trim().slice(-MAX_STDERR_DETAIL_LENGTH);
|
|
32409
|
+
throw new GuestExecutableProbeError("capability_probe_failed", `Guest executable capability probe failed (exit ${result.exitCode})${stderr ? `: ${stderr}` : ""}`);
|
|
32410
|
+
}
|
|
32411
|
+
const availableIndexes = new Set(result.stdout.split("\n").filter(Boolean).map((value) => Number.parseInt(value, 10)).filter((value) => Number.isSafeInteger(value) && value >= 0));
|
|
32412
|
+
return {
|
|
32413
|
+
available: unique.filter((_, index) => availableIndexes.has(index)),
|
|
32414
|
+
unavailable: unique.filter((_, index) => !availableIndexes.has(index))
|
|
32415
|
+
};
|
|
32416
|
+
}
|
|
32417
|
+
//#endregion
|
|
32418
|
+
//#region src/runtime/model-selection.ts
|
|
32419
|
+
var RuntimeProfileModelResolutionError = class extends Error {
|
|
32420
|
+
constructor(message) {
|
|
32421
|
+
super(message);
|
|
32422
|
+
this.name = "RuntimeProfileModelResolutionError";
|
|
32423
|
+
}
|
|
32424
|
+
};
|
|
32425
|
+
/**
|
|
32426
|
+
* Resolve the exact runtime-profile model through Pi's custom-model registry.
|
|
32427
|
+
*
|
|
32428
|
+
* `getModel()` from pi-ai only knows generated built-in models. Runtime
|
|
32429
|
+
* profiles commonly select providers declared in the active Pi directory's
|
|
32430
|
+
* models.json, so an unresolved lookup must fail closed rather than letting
|
|
32431
|
+
* createAgentSession silently choose the default from settings.json.
|
|
32432
|
+
*/
|
|
32433
|
+
function resolveRuntimeProfileModel(piAuthDir, provider, modelId, runtimeProfileId) {
|
|
32434
|
+
const authStorage = AuthStorage.create(join(piAuthDir, "auth.json"));
|
|
32435
|
+
const modelsPath = join(piAuthDir, "models.json");
|
|
32436
|
+
const modelRegistry = ModelRegistry.create(authStorage, modelsPath);
|
|
32437
|
+
const modelHandle = modelRegistry.find(provider, modelId);
|
|
32438
|
+
if (!modelHandle) {
|
|
32439
|
+
const registryError = modelRegistry.getError();
|
|
32440
|
+
const detail = registryError ? ` Registry error: ${registryError}` : "";
|
|
32441
|
+
const alternatives = modelRegistry.getAvailable().slice(0, 8).map((model) => `${model.provider}/${model.id}`);
|
|
32442
|
+
throw new RuntimeProfileModelResolutionError(`${runtimeProfileId ? `Runtime profile "${runtimeProfileId}"` : "Runtime profile"} model "${provider}/${modelId}" was not found in ${modelsPath}; refusing Pi default-model fallback.` + (alternatives.length > 0 ? ` Available models include: ${alternatives.join(", ")}.` : "") + detail);
|
|
32443
|
+
}
|
|
32444
|
+
return {
|
|
32445
|
+
modelHandle,
|
|
32446
|
+
modelRegistry
|
|
32447
|
+
};
|
|
32448
|
+
}
|
|
32449
|
+
//#endregion
|
|
32690
32450
|
//#region src/runtime/resolve-prior-context.ts
|
|
32691
32451
|
/**
|
|
32692
32452
|
* Fetch the named attempt's output and project it into the prompt's
|
|
@@ -32873,6 +32633,40 @@ async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
|
32873
32633
|
}
|
|
32874
32634
|
}
|
|
32875
32635
|
//#endregion
|
|
32636
|
+
//#region src/runtime/runtime-capability-projection.ts
|
|
32637
|
+
/**
|
|
32638
|
+
* Project trusted policy resolution and runtime inventory into the exact
|
|
32639
|
+
* model-visible policy plus host-side degradation diagnostics.
|
|
32640
|
+
*/
|
|
32641
|
+
function projectRuntimeCapabilities(input) {
|
|
32642
|
+
const visibleOptionalTools = input.visibleToolNames.filter((name) => !name.startsWith("submit_"));
|
|
32643
|
+
if (!input.policy) return {
|
|
32644
|
+
instructorPolicy: {
|
|
32645
|
+
enforcement: "off",
|
|
32646
|
+
allowedTools: visibleOptionalTools,
|
|
32647
|
+
allowedShellCommands: [],
|
|
32648
|
+
degraded: false
|
|
32649
|
+
},
|
|
32650
|
+
unavailableTools: [],
|
|
32651
|
+
unavailableExecutables: [],
|
|
32652
|
+
droppedShellCommandCount: 0
|
|
32653
|
+
};
|
|
32654
|
+
const visible = new Set(input.visibleToolNames);
|
|
32655
|
+
const unavailableTools = [...input.policy.allowedTools].filter((name) => !visible.has(name)).sort();
|
|
32656
|
+
const unavailableShellCommands = input.unavailableShellCommands ?? [];
|
|
32657
|
+
return {
|
|
32658
|
+
instructorPolicy: {
|
|
32659
|
+
enforcement: input.policy.enforcement,
|
|
32660
|
+
allowedTools: visibleOptionalTools,
|
|
32661
|
+
allowedShellCommands: input.policy.allowedShellCommands,
|
|
32662
|
+
degraded: input.policy.degraded
|
|
32663
|
+
},
|
|
32664
|
+
unavailableTools,
|
|
32665
|
+
unavailableExecutables: [...new Set(unavailableShellCommands.map(({ argvPrefix }) => argvPrefix[0]))].sort(),
|
|
32666
|
+
droppedShellCommandCount: unavailableShellCommands.length
|
|
32667
|
+
};
|
|
32668
|
+
}
|
|
32669
|
+
//#endregion
|
|
32876
32670
|
//#region src/runtime/runtime-context.ts
|
|
32877
32671
|
/**
|
|
32878
32672
|
* Pi-specific runtime context handling.
|
|
@@ -32997,6 +32791,73 @@ function buildWorkspaceMountInstructions(guestWorkspace) {
|
|
|
32997
32791
|
" non-existent checkout."
|
|
32998
32792
|
].join("\n");
|
|
32999
32793
|
}
|
|
32794
|
+
function buildToolPolicyInstructions(policy) {
|
|
32795
|
+
const lines = [
|
|
32796
|
+
"## Effective runtime tool policy",
|
|
32797
|
+
"",
|
|
32798
|
+
"- The registered submit-output tool is always the completion protocol."
|
|
32799
|
+
];
|
|
32800
|
+
if (!policy || policy.enforcement === "off") {
|
|
32801
|
+
lines.push("- Enforcement is off: runtime policy does not restrict visible tools or", " shell commands. The live sandbox still determines what is installed.");
|
|
32802
|
+
return lines.join("\n");
|
|
32803
|
+
}
|
|
32804
|
+
lines.push(`- Enforcement mode: \`${policy.enforcement}\`.`);
|
|
32805
|
+
if (policy.degraded) lines.push("- Policy resolution degraded. Enforce mode is fail-closed; only the", " kernel submit-output tool is available.");
|
|
32806
|
+
lines.push(policy.allowedTools.length > 0 ? "- The visible structured-tool definitions are the authorized surface." : policy.enforcement === "enforce" ? "- No optional structured tools are authorized." : "- No optional structured tools are registered.");
|
|
32807
|
+
if (policy.enforcement === "watch") lines.push("- Watch mode records policy decisions but does not block tool calls.");
|
|
32808
|
+
else if (policy.allowedShellCommands.length === 0) lines.push("- No shell commands are authorized. `bash` is not available; do not", " attempt shell, filesystem, git, GitHub CLI, or MoltNet CLI commands.");
|
|
32809
|
+
else lines.push("- Shell commands are restricted to these authorized argv prefixes:", ...renderShellCommandPrefixes(policy.allowedShellCommands), "- A visible `bash` tool does not grant broader shell authority. Do not", " attempt commands outside those prefixes.");
|
|
32810
|
+
lines.push("- Tools and commands absent from this effective policy are unavailable,", " even if advisory context mentions them.");
|
|
32811
|
+
return lines.join("\n");
|
|
32812
|
+
}
|
|
32813
|
+
function renderShellCommandPrefixes(commands) {
|
|
32814
|
+
const visible = commands.slice(0, 12).map(({ argvPrefix }) => ` - \`${argvPrefix.join(" ")}\``);
|
|
32815
|
+
const omitted = commands.length - visible.length;
|
|
32816
|
+
if (omitted > 0) visible.push(` - …and ${omitted} more authorized prefixes.`);
|
|
32817
|
+
return visible;
|
|
32818
|
+
}
|
|
32819
|
+
function buildSandboxCapabilityInstructions(sandbox, policy) {
|
|
32820
|
+
if (!sandbox) return "";
|
|
32821
|
+
const executableLines = !policy || policy.enforcement === "off" ? ["- Executables are discovered through the visible shell when needed."] : [sandbox.verifiedExecutables.length > 0 ? `- Session-verified policy executables: ${sandbox.verifiedExecutables.map((name) => `\`${name}\``).join(", ")}.` : "- No policy-relevant guest executables were verified for this session.", ...policy.enforcement === "watch" ? ["- Watch-mode probes are diagnostic, not an executable", " allowlist; other installed commands remain policy-permitted."] : []];
|
|
32822
|
+
const lines = [
|
|
32823
|
+
"## Effective sandbox capabilities",
|
|
32824
|
+
"",
|
|
32825
|
+
`- Workspace mode: \`${sandbox.workspaceMode}\`.`,
|
|
32826
|
+
`- VFS shadow mode: \`${sandbox.vfsShadowMode}\`${sandbox.vfsShadowPatterns.length > 0 ? ` for ${sandbox.vfsShadowPatterns.map((pattern) => `\`${pattern}\``).join(", ")}` : ""}.`,
|
|
32827
|
+
...sandbox.nodeModulesWriteMode ? ["- `node_modules` writes use session-local tmpfs and do not persist", " to the mounted workspace."] : [],
|
|
32828
|
+
...executableLines
|
|
32829
|
+
];
|
|
32830
|
+
const externalHosts = [...sandbox.allowedHosts].sort();
|
|
32831
|
+
const internalHosts = [...sandbox.allowedInternalHosts].sort();
|
|
32832
|
+
lines.push(...externalHosts.length > 0 ? [`- Additional external egress hosts: ${externalHosts.map((host) => `\`${host}\``).join(", ")}.`] : [], ...internalHosts.length > 0 ? [`- Additional internal egress hosts: ${internalHosts.map((host) => `\`${host}\``).join(", ")}.`] : [], "- Runtime service endpoints required for task execution may be available", " in addition to the operator-configured hosts above.");
|
|
32833
|
+
return lines.join("\n");
|
|
32834
|
+
}
|
|
32835
|
+
function shellExecutableIsAvailable(policy, sandbox, executable) {
|
|
32836
|
+
if (!policy || policy.enforcement === "off") return true;
|
|
32837
|
+
if (sandbox && !sandbox.verifiedExecutables.includes(executable)) return false;
|
|
32838
|
+
if (policy.enforcement !== "enforce") return true;
|
|
32839
|
+
return policy.allowedShellCommands.some(({ argvPrefix }) => argvPrefix[0] === executable);
|
|
32840
|
+
}
|
|
32841
|
+
function buildCredentialInstructions(policy, sandbox) {
|
|
32842
|
+
const lines = [
|
|
32843
|
+
"## Identity & credentials",
|
|
32844
|
+
"",
|
|
32845
|
+
"- Your credentials live at `/home/agent/.moltnet/<agent>/moltnet.json`",
|
|
32846
|
+
" with the gitconfig and SSH key alongside. Do not move, copy, or expose",
|
|
32847
|
+
" these files outside the VM."
|
|
32848
|
+
];
|
|
32849
|
+
const moltnetAvailable = shellExecutableIsAvailable(policy, sandbox, "moltnet");
|
|
32850
|
+
const ghAvailable = shellExecutableIsAvailable(policy, sandbox, "gh");
|
|
32851
|
+
const gitAvailable = shellExecutableIsAvailable(policy, sandbox, "git");
|
|
32852
|
+
if (moltnetAvailable) lines.push("- When authorized by the effective shell policy, use the installed", " `moltnet` binary on `PATH`; never invoke a cached or `npx` copy.");
|
|
32853
|
+
if (ghAvailable) {
|
|
32854
|
+
lines.push("- This headless VM has no human GitHub token fallback. Every authorized", " `gh` write must use an inline App token.");
|
|
32855
|
+
if (moltnetAvailable) lines.push("", " ```bash", " CREDS=\"$(cd \"$(dirname \"$GIT_CONFIG_GLOBAL\")\" && pwd)/moltnet.json\"", " GH_TOKEN=$(moltnet github token --credentials \"$CREDS\") gh <command>", " ```");
|
|
32856
|
+
else lines.push("- The effective policy does not authorize the `moltnet` token-minting", " command, so do not attempt a GitHub write.");
|
|
32857
|
+
}
|
|
32858
|
+
if (gitAvailable) lines.push("- An authorized `git push` uses the injected credential helper and does", " not need `GH_TOKEN`.");
|
|
32859
|
+
return lines.join("\n");
|
|
32860
|
+
}
|
|
33000
32861
|
/**
|
|
33001
32862
|
* Build the minimal immutable system-prompt kernel. Runtime-profile context
|
|
33002
32863
|
* carries operator-selected workflow guidance; this kernel stays last in the
|
|
@@ -33019,31 +32880,7 @@ function buildRuntimeKernel(ctx) {
|
|
|
33019
32880
|
`- Diary id (for this task): \`${ctx.diaryId}\``,
|
|
33020
32881
|
`- Agent name: \`${ctx.agentName}\``,
|
|
33021
32882
|
"",
|
|
33022
|
-
|
|
33023
|
-
"",
|
|
33024
|
-
"- Your credentials live at `/home/agent/.moltnet/<agent>/moltnet.json`",
|
|
33025
|
-
" with the gitconfig and SSH key alongside. Do not move, copy, or expose",
|
|
33026
|
-
" these files outside the VM.",
|
|
33027
|
-
"- The `moltnet` CLI is installed in the VM and is the only supported way",
|
|
33028
|
-
" to mint short-lived tokens. Do not invoke `npx @themoltnet/cli` or any",
|
|
33029
|
-
" cached path — use the `moltnet` binary on `PATH`.",
|
|
33030
|
-
"- Interactive sessions use the canonical `moltnet github guard` policy,",
|
|
33031
|
-
" documented in `docs/reference/agent-configuration.md`. This headless VM",
|
|
33032
|
-
" has no editor hook and no human GitHub token to fall back to: read-only",
|
|
33033
|
-
" `gh` commands may run bare, but every write must use the App token:",
|
|
33034
|
-
"",
|
|
33035
|
-
" ```bash",
|
|
33036
|
-
" CREDS=\"$(cd \"$(dirname \"$GIT_CONFIG_GLOBAL\")\" && pwd)/moltnet.json\"",
|
|
33037
|
-
" GH_TOKEN=$(moltnet github token --credentials \"$CREDS\") gh <command>",
|
|
33038
|
-
" ```",
|
|
33039
|
-
"",
|
|
33040
|
-
"- `git push` uses the gitconfig-configured credential helper and is not",
|
|
33041
|
-
" a `gh` call — it does not need `GH_TOKEN`.",
|
|
33042
|
-
"- Run `git` and `gh` in the VM with your normal `bash` tool — your",
|
|
33043
|
-
" credentials are injected here, so they work in the guest. The",
|
|
33044
|
-
" `moltnet_host_exec` tool is a last-resort host escape-hatch that",
|
|
33045
|
-
" requires human approval and is unavailable in headless task runs;",
|
|
33046
|
-
" never use it for routine git/gh.",
|
|
32883
|
+
buildCredentialInstructions(ctx.toolPolicy, ctx.sandbox),
|
|
33047
32884
|
"",
|
|
33048
32885
|
"## Skill packs",
|
|
33049
32886
|
"",
|
|
@@ -33056,6 +32893,10 @@ function buildRuntimeKernel(ctx) {
|
|
|
33056
32893
|
"",
|
|
33057
32894
|
buildWorkspaceMountInstructions(ctx.guestWorkspace),
|
|
33058
32895
|
"",
|
|
32896
|
+
buildSandboxCapabilityInstructions(ctx.sandbox, ctx.toolPolicy),
|
|
32897
|
+
...ctx.sandbox ? [""] : [],
|
|
32898
|
+
buildToolPolicyInstructions(ctx.toolPolicy),
|
|
32899
|
+
"",
|
|
33059
32900
|
"## Structured completion",
|
|
33060
32901
|
"- The registered submit-output tool is the only completion wire protocol. Submit its typed payload when work is complete; prose is not a substitute."
|
|
33061
32902
|
].join("\n");
|
|
@@ -33167,6 +33008,7 @@ function createSubagentTool(args) {
|
|
|
33167
33008
|
cwdPath: args.cwdPath ?? args.mountPath,
|
|
33168
33009
|
piAuthDir: args.piAuthDir,
|
|
33169
33010
|
modelHandle: args.modelHandle,
|
|
33011
|
+
modelRegistry: args.modelRegistry,
|
|
33170
33012
|
thinkingLevel: args.thinkingLevel,
|
|
33171
33013
|
temperature: args.temperature,
|
|
33172
33014
|
topP: args.topP,
|
|
@@ -33294,6 +33136,39 @@ function toolError(text, details = { captured: false }) {
|
|
|
33294
33136
|
};
|
|
33295
33137
|
}
|
|
33296
33138
|
//#endregion
|
|
33139
|
+
//#region src/runtime/submit-completion-coordinator.ts
|
|
33140
|
+
/**
|
|
33141
|
+
* Delay the post-submit session abort until every tool call in Pi's current
|
|
33142
|
+
* parallel batch has emitted `tool_execution_end`. A valid submit call can
|
|
33143
|
+
* finish before a sibling write or artifact upload; aborting immediately would
|
|
33144
|
+
* cancel that sibling after the runtime had already accepted the output.
|
|
33145
|
+
*/
|
|
33146
|
+
function createSubmitCompletionCoordinator(options) {
|
|
33147
|
+
const activeToolCalls = /* @__PURE__ */ new Set();
|
|
33148
|
+
let completionRequested = false;
|
|
33149
|
+
let completionStarted = false;
|
|
33150
|
+
const drain = () => {
|
|
33151
|
+
if (!completionRequested || completionStarted || activeToolCalls.size > 0) return;
|
|
33152
|
+
completionStarted = true;
|
|
33153
|
+
Promise.resolve(options.onDrained()).catch(options.onError);
|
|
33154
|
+
};
|
|
33155
|
+
return {
|
|
33156
|
+
extension: (pi) => {
|
|
33157
|
+
pi.on("tool_execution_start", (event) => {
|
|
33158
|
+
activeToolCalls.add(event.toolCallId);
|
|
33159
|
+
});
|
|
33160
|
+
pi.on("tool_execution_end", (event) => {
|
|
33161
|
+
activeToolCalls.delete(event.toolCallId);
|
|
33162
|
+
drain();
|
|
33163
|
+
});
|
|
33164
|
+
},
|
|
33165
|
+
requestCompletion: () => {
|
|
33166
|
+
completionRequested = true;
|
|
33167
|
+
drain();
|
|
33168
|
+
}
|
|
33169
|
+
};
|
|
33170
|
+
}
|
|
33171
|
+
//#endregion
|
|
33297
33172
|
//#region src/runtime/task-output.ts
|
|
33298
33173
|
var METER_NAME = "@themoltnet/pi-extension/task-output";
|
|
33299
33174
|
var parseResultCounter = null;
|
|
@@ -33463,7 +33338,8 @@ function submitOutputRepairHint(taskType, errors) {
|
|
|
33463
33338
|
const hints = ["Tool args must be the output object directly, not wrapped in { output: ... }."];
|
|
33464
33339
|
if (fields.has("output/artifacts")) hints.push("`artifacts` must be an array; omit it when there are no artifacts, use [], or use objects like { \"kind\": \"note\", \"title\": \"Result\", \"body\": \"...\" }.");
|
|
33465
33340
|
if (fields.has("output/verification")) hints.push("`verification` must be an object with inputCid, results[], and passed; do not send it as text or an array.");
|
|
33466
|
-
if (
|
|
33341
|
+
if (fields.has("output/artifacts") || fields.has("output/verification")) if (taskType === "freeform") hints.push("Minimal valid freeform retry: { \"summary\": \"completed\", \"artifacts\": [], \"verification\": { \"inputCid\": \"<task inputCid>\", \"results\": [{ \"id\": \"submit-output\", \"kind\": \"gate\", \"status\": \"pass\", \"detail\": \"submit_freeform_output accepted valid args\" }], \"passed\": true } }.");
|
|
33342
|
+
else hints.push(`\`verification\` is a stamp when the only gate is submit-output: { "inputCid": "<task inputCid>", "results": [{ "id": "submit-output", "kind": "gate", "status": "pass", "detail": "submit_${taskType}_output accepted valid args" }], "passed": true }.`);
|
|
33467
33343
|
if (hints.length === 1) hints.push("Fix every listed field before re-calling this same tool.");
|
|
33468
33344
|
return hints.join(" ");
|
|
33469
33345
|
}
|
|
@@ -33481,7 +33357,18 @@ function onlySubmitOutputGate(input) {
|
|
|
33481
33357
|
if (Array.isArray(assertions) && assertions.length > 0) return false;
|
|
33482
33358
|
return criteria.rubric === void 0 && criteria.sideEffects === void 0 && criteria.minComposite === void 0;
|
|
33483
33359
|
}
|
|
33484
|
-
|
|
33360
|
+
/**
|
|
33361
|
+
* Repair a producer submit-output payload when the task's ONLY success gate is
|
|
33362
|
+
* the auto-injected submit-output gate — i.e. there is nothing substantive to
|
|
33363
|
+
* self-assess, so the `verification` record is a mechanical stamp. Applies to
|
|
33364
|
+
* any producer task type (freeform, run_eval, …), not just freeform: weaker
|
|
33365
|
+
* models mis-type or omit the nested `verification` object identically across
|
|
33366
|
+
* types, and previously only freeform was repaired, so run_eval attempts failed
|
|
33367
|
+
* `output_validation_failed` on verification alone. The freeform-only field
|
|
33368
|
+
* coercions below are guarded by field presence, so they no-op for other types,
|
|
33369
|
+
* and the caller re-validates the repaired payload against the type's schema.
|
|
33370
|
+
*/
|
|
33371
|
+
function repairProducerSubmitOutput(taskType, params, opts) {
|
|
33485
33372
|
if (!isRecord(params) || !opts.inputCid || !onlySubmitOutputGate(opts.input)) return null;
|
|
33486
33373
|
const repaired = { ...params };
|
|
33487
33374
|
if ("artifacts" in repaired && !Array.isArray(repaired.artifacts)) if (isRecord(repaired.artifacts)) repaired.artifacts = [repaired.artifacts];
|
|
@@ -33497,15 +33384,14 @@ function repairFreeformSubmitOutput(params, opts) {
|
|
|
33497
33384
|
id: SUBMIT_OUTPUT_GATE_ID,
|
|
33498
33385
|
kind: "gate",
|
|
33499
33386
|
status: "pass",
|
|
33500
|
-
detail:
|
|
33387
|
+
detail: `submit_${taskType}_output accepted valid args`
|
|
33501
33388
|
}],
|
|
33502
33389
|
passed: true
|
|
33503
33390
|
};
|
|
33504
33391
|
return repaired;
|
|
33505
33392
|
}
|
|
33506
33393
|
function maybeRepairSubmitOutput(taskType, params, opts) {
|
|
33507
|
-
|
|
33508
|
-
const repaired = repairFreeformSubmitOutput(params, opts);
|
|
33394
|
+
const repaired = repairProducerSubmitOutput(taskType, params, opts);
|
|
33509
33395
|
if (!repaired) return null;
|
|
33510
33396
|
return validateTaskSubmission(taskType, repaired, opts.input, { inputCid: opts.inputCid }).length === 0 ? repaired : null;
|
|
33511
33397
|
}
|
|
@@ -33529,10 +33415,24 @@ Agent submission schema:\n\`\`\`json\n${contract.parametersSchemaJson}\n\`\`\``,
|
|
|
33529
33415
|
promptGuidelines: [
|
|
33530
33416
|
`Call \`${contract.toolName}\` with the exact ${taskType} agent submission shape shown above.`,
|
|
33531
33417
|
"The transport accepts malformed objects only so validation errors can be recovered in-session; the schema shown above is authoritative.",
|
|
33532
|
-
"If the submit tool returns a validation error, fix every listed field and call the same tool again."
|
|
33418
|
+
"If the submit tool returns a validation error, fix every listed field and call the same tool again.",
|
|
33419
|
+
"The first valid submission is final and immediately ends the session."
|
|
33533
33420
|
],
|
|
33534
33421
|
parameters: RecoverableSubmitToolParameters,
|
|
33535
33422
|
async execute(_id, params) {
|
|
33423
|
+
if (captured) return {
|
|
33424
|
+
content: [{
|
|
33425
|
+
type: "text",
|
|
33426
|
+
text: "Output was already captured. The first valid payload remains final; this duplicate submission was ignored."
|
|
33427
|
+
}],
|
|
33428
|
+
details: {
|
|
33429
|
+
captured: true,
|
|
33430
|
+
callCount,
|
|
33431
|
+
invalidCallCount,
|
|
33432
|
+
maxSubmitValidationRetries,
|
|
33433
|
+
error: null
|
|
33434
|
+
}
|
|
33435
|
+
};
|
|
33536
33436
|
if (exhaustedValidationFailure) return {
|
|
33537
33437
|
content: [{
|
|
33538
33438
|
type: "text",
|
|
@@ -33583,6 +33483,7 @@ Agent submission schema:\n\`\`\`json\n${contract.parametersSchemaJson}\n\`\`\``,
|
|
|
33583
33483
|
}
|
|
33584
33484
|
captured = candidateParams;
|
|
33585
33485
|
callCount += 1;
|
|
33486
|
+
await opts.onValidCapture?.();
|
|
33586
33487
|
return {
|
|
33587
33488
|
content: [{
|
|
33588
33489
|
type: "text",
|
|
@@ -34023,7 +33924,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34023
33924
|
let subagentHandle = null;
|
|
34024
33925
|
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
34025
33926
|
let cancelListener = null;
|
|
34026
|
-
const makeFailedOutput = (code, message, usage = finalUsage) => ({
|
|
33927
|
+
const makeFailedOutput = (code, message, usage = finalUsage, retryable = false) => ({
|
|
34027
33928
|
taskId: task.id,
|
|
34028
33929
|
attemptN,
|
|
34029
33930
|
status: "failed",
|
|
@@ -34034,7 +33935,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34034
33935
|
error: {
|
|
34035
33936
|
code,
|
|
34036
33937
|
message,
|
|
34037
|
-
retryable
|
|
33938
|
+
retryable
|
|
34038
33939
|
}
|
|
34039
33940
|
});
|
|
34040
33941
|
const makeCancelledOutput = (message) => ({
|
|
@@ -34087,6 +33988,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34087
33988
|
reporterOpen = true;
|
|
34088
33989
|
let checkpointPath;
|
|
34089
33990
|
let resolvedVmTemplate = opts.resolvedVmTemplate;
|
|
33991
|
+
let effectiveSandboxConfig;
|
|
34090
33992
|
try {
|
|
34091
33993
|
if (!resolvedVmTemplate && opts.runtimeDefinition) resolvedVmTemplate = opts.resolveVmTemplate ? await opts.resolveVmTemplate() : await opts.runtimeDefinition.vm.resolve({ onProgress: opts.onSnapshotProgress });
|
|
34092
33994
|
checkpointPath = resolvedVmTemplate?.checkpointPath ?? opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
|
|
@@ -34110,7 +34012,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34110
34012
|
return makeFailedOutput("worktree_setup_failed", message);
|
|
34111
34013
|
}
|
|
34112
34014
|
try {
|
|
34113
|
-
|
|
34015
|
+
effectiveSandboxConfig = applyExecutionPlanSandboxOverrides(resolvedVmTemplate ? {
|
|
34114
34016
|
...opts.sandboxConfig,
|
|
34115
34017
|
snapshot: void 0,
|
|
34116
34018
|
resumeCommands: [...resolvedVmTemplate.resumeCommands]
|
|
@@ -34122,7 +34024,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34122
34024
|
mountPath,
|
|
34123
34025
|
workspaceMode: workspace.mode,
|
|
34124
34026
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
34125
|
-
sandboxConfig,
|
|
34027
|
+
sandboxConfig: effectiveSandboxConfig,
|
|
34126
34028
|
forwardEnv: opts.forwardEnv,
|
|
34127
34029
|
signal: reporter.cancelSignal
|
|
34128
34030
|
});
|
|
@@ -34252,11 +34154,20 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34252
34154
|
cwdPath,
|
|
34253
34155
|
guestWorkspace: managed.guestWorkspace
|
|
34254
34156
|
});
|
|
34157
|
+
const submitCompletion = createSubmitCompletionCoordinator({
|
|
34158
|
+
onDrained: () => session?.abort(),
|
|
34159
|
+
onError: async (err) => {
|
|
34160
|
+
await emitError("submit_output_abort", err instanceof Error ? err.message : String(err), { event: "submit_output_abort_failed" });
|
|
34161
|
+
}
|
|
34162
|
+
});
|
|
34255
34163
|
const { handle: submitToolHandle, tools: submitToolDefs } = resolveSubmitTools(task.taskType, {
|
|
34256
34164
|
model: opts.model,
|
|
34257
34165
|
input: task.input,
|
|
34258
34166
|
inputCid: task.inputCid,
|
|
34259
|
-
maxSubmitValidationRetries: opts.maxSubmitValidationRetries
|
|
34167
|
+
maxSubmitValidationRetries: opts.maxSubmitValidationRetries,
|
|
34168
|
+
onValidCapture: () => {
|
|
34169
|
+
submitCompletion.requestCompletion();
|
|
34170
|
+
}
|
|
34260
34171
|
});
|
|
34261
34172
|
const submitTools = submitToolDefs;
|
|
34262
34173
|
try {
|
|
@@ -34284,38 +34195,27 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34284
34195
|
correlationId: task.correlationId ?? null
|
|
34285
34196
|
})
|
|
34286
34197
|
});
|
|
34287
|
-
const piAuthDir =
|
|
34288
|
-
const modelHandle =
|
|
34289
|
-
const runtimeKernel = buildRuntimeKernel({
|
|
34290
|
-
taskId: task.id,
|
|
34291
|
-
taskType: task.taskType,
|
|
34292
|
-
attemptN,
|
|
34293
|
-
diaryId,
|
|
34294
|
-
agentName: opts.agentName,
|
|
34295
|
-
guestWorkspace: managed.guestWorkspace,
|
|
34296
|
-
correlationId: task.correlationId ?? null
|
|
34297
|
-
});
|
|
34298
|
-
const appendSystemPrompt = composeRuntimeSystemPrompt({
|
|
34299
|
-
profilePromptPrefix: injectedContext.systemPromptPrefix,
|
|
34300
|
-
kernel: runtimeKernel
|
|
34301
|
-
});
|
|
34198
|
+
const piAuthDir = resolvePiCodingAgentDir();
|
|
34199
|
+
const { modelHandle, modelRegistry } = resolveRuntimeProfileModel(piAuthDir, opts.provider, opts.model, opts.runtimeProfileId);
|
|
34302
34200
|
const injectedSkills = injectedContext.skills;
|
|
34303
34201
|
const toolPolicyExtensions = [];
|
|
34304
34202
|
let resolvedToolPolicy;
|
|
34203
|
+
let unavailableRuntimeShellCommands = [];
|
|
34204
|
+
let verifiedGuestExecutables = [];
|
|
34205
|
+
const toolPolicyLogger = opts.toolPolicyLogger ?? {
|
|
34206
|
+
debug: () => {},
|
|
34207
|
+
info: (obj, msg) => console.error(JSON.stringify({
|
|
34208
|
+
level: "info",
|
|
34209
|
+
msg,
|
|
34210
|
+
...obj
|
|
34211
|
+
})),
|
|
34212
|
+
warn: (obj, msg) => console.error(JSON.stringify({
|
|
34213
|
+
level: "warn",
|
|
34214
|
+
msg,
|
|
34215
|
+
...obj
|
|
34216
|
+
}))
|
|
34217
|
+
};
|
|
34305
34218
|
if (opts.runtimeProfileId && opts.toolEnforcement && opts.toolEnforcement !== "off") {
|
|
34306
|
-
const toolPolicyLogger = opts.toolPolicyLogger ?? {
|
|
34307
|
-
debug: () => {},
|
|
34308
|
-
info: (obj, msg) => console.error(JSON.stringify({
|
|
34309
|
-
level: "info",
|
|
34310
|
-
msg,
|
|
34311
|
-
...obj
|
|
34312
|
-
})),
|
|
34313
|
-
warn: (obj, msg) => console.error(JSON.stringify({
|
|
34314
|
-
level: "warn",
|
|
34315
|
-
msg,
|
|
34316
|
-
...obj
|
|
34317
|
-
}))
|
|
34318
|
-
};
|
|
34319
34219
|
const [analyzer, policy] = await Promise.all([ShellCommandAnalyzer.create(), resolveSessionToolPolicy({
|
|
34320
34220
|
agent: moltnetAgent,
|
|
34321
34221
|
profileId: opts.runtimeProfileId,
|
|
@@ -34324,9 +34224,16 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34324
34224
|
enforcement: opts.toolEnforcement,
|
|
34325
34225
|
logger: toolPolicyLogger
|
|
34326
34226
|
})]);
|
|
34327
|
-
|
|
34227
|
+
verifiedGuestExecutables = (await discoverGuestExecutables(managed.vm, [...policy.allowedShellCommands.map(({ argvPrefix }) => argvPrefix[0]), ...policy.enforcement === "watch" ? resolvedVmTemplate?.executables ?? [] : []], { signal: reporter.cancelSignal })).available;
|
|
34228
|
+
const availableExecutables = new Set(verifiedGuestExecutables);
|
|
34229
|
+
const allowedShellCommands = policy.allowedShellCommands.filter(({ argvPrefix }) => availableExecutables.has(argvPrefix[0]));
|
|
34230
|
+
unavailableRuntimeShellCommands = policy.allowedShellCommands.filter(({ argvPrefix }) => !availableExecutables.has(argvPrefix[0]));
|
|
34231
|
+
resolvedToolPolicy = {
|
|
34232
|
+
...policy,
|
|
34233
|
+
allowedShellCommands
|
|
34234
|
+
};
|
|
34328
34235
|
toolPolicyExtensions.push(createToolPolicyExtension({
|
|
34329
|
-
policy,
|
|
34236
|
+
policy: resolvedToolPolicy,
|
|
34330
34237
|
analyzer,
|
|
34331
34238
|
logger: toolPolicyLogger
|
|
34332
34239
|
}));
|
|
@@ -34364,13 +34271,67 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34364
34271
|
policy: resolvedToolPolicy
|
|
34365
34272
|
}) : [];
|
|
34366
34273
|
const visibleBaseTools = filterModelVisibleTools([...gondolinCustomTools, ...moltnetTools], resolvedToolPolicy);
|
|
34274
|
+
const taskHasSubagents = taskTypeUsesSubagents(task.taskType);
|
|
34275
|
+
const visibleParentToolNames = modelVisiblePiToolNames({
|
|
34276
|
+
tools: [
|
|
34277
|
+
...visibleBaseTools,
|
|
34278
|
+
...runtimeParentTools,
|
|
34279
|
+
...submitTools,
|
|
34280
|
+
...taskHasSubagents ? [{ name: "subagent" }] : []
|
|
34281
|
+
],
|
|
34282
|
+
extensions: opts.runtimeDefinition?.extensions,
|
|
34283
|
+
policy: resolvedToolPolicy
|
|
34284
|
+
});
|
|
34285
|
+
const capabilityProjection = projectRuntimeCapabilities({
|
|
34286
|
+
policy: resolvedToolPolicy,
|
|
34287
|
+
visibleToolNames: visibleParentToolNames,
|
|
34288
|
+
unavailableShellCommands: unavailableRuntimeShellCommands
|
|
34289
|
+
});
|
|
34290
|
+
if (capabilityProjection.unavailableTools.length > 0 || capabilityProjection.unavailableExecutables.length > 0) {
|
|
34291
|
+
const diagnostics = {
|
|
34292
|
+
runtimeProfileId: opts.runtimeProfileId,
|
|
34293
|
+
unavailableTools: capabilityProjection.unavailableTools,
|
|
34294
|
+
unavailableExecutables: capabilityProjection.unavailableExecutables,
|
|
34295
|
+
droppedShellCommandCount: capabilityProjection.droppedShellCommandCount
|
|
34296
|
+
};
|
|
34297
|
+
toolPolicyLogger.warn(diagnostics, "Runtime policy grants unavailable capabilities");
|
|
34298
|
+
await emit("info", {
|
|
34299
|
+
event: "runtime_capabilities_unavailable",
|
|
34300
|
+
...diagnostics
|
|
34301
|
+
});
|
|
34302
|
+
}
|
|
34303
|
+
const vfsShadow = resolveVfsShadowConfig(effectiveSandboxConfig);
|
|
34304
|
+
const runtimeKernel = buildRuntimeKernel({
|
|
34305
|
+
taskId: task.id,
|
|
34306
|
+
taskType: task.taskType,
|
|
34307
|
+
attemptN,
|
|
34308
|
+
diaryId,
|
|
34309
|
+
agentName: opts.agentName,
|
|
34310
|
+
guestWorkspace: managed.guestWorkspace,
|
|
34311
|
+
correlationId: task.correlationId ?? null,
|
|
34312
|
+
sandbox: {
|
|
34313
|
+
workspaceMode: activeWorkspace.mode,
|
|
34314
|
+
vfsShadowMode: vfsShadow.mode,
|
|
34315
|
+
vfsShadowPatterns: vfsShadow.patterns,
|
|
34316
|
+
nodeModulesWriteMode: "tmpfs",
|
|
34317
|
+
verifiedExecutables: verifiedGuestExecutables,
|
|
34318
|
+
allowedHosts: [...effectiveSandboxConfig?.network?.allowedHosts ?? [], ...opts.extraAllowedHosts ?? []],
|
|
34319
|
+
allowedInternalHosts: effectiveSandboxConfig?.network?.allowedInternalHosts ?? []
|
|
34320
|
+
},
|
|
34321
|
+
toolPolicy: capabilityProjection.instructorPolicy
|
|
34322
|
+
});
|
|
34323
|
+
const appendSystemPrompt = composeRuntimeSystemPrompt({
|
|
34324
|
+
profilePromptPrefix: injectedContext.systemPromptPrefix,
|
|
34325
|
+
kernel: runtimeKernel
|
|
34326
|
+
});
|
|
34367
34327
|
const parentSubagentTools = [];
|
|
34368
|
-
if (
|
|
34328
|
+
if (taskHasSubagents) {
|
|
34369
34329
|
subagentHandle = createSubagentTool({
|
|
34370
34330
|
mountPath,
|
|
34371
34331
|
cwdPath,
|
|
34372
34332
|
piAuthDir,
|
|
34373
34333
|
modelHandle,
|
|
34334
|
+
modelRegistry,
|
|
34374
34335
|
thinkingLevel: opts.thinkingLevel,
|
|
34375
34336
|
temperature: opts.temperature,
|
|
34376
34337
|
topP: opts.topP,
|
|
@@ -34404,6 +34365,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34404
34365
|
cwdPath,
|
|
34405
34366
|
piAuthDir,
|
|
34406
34367
|
modelHandle,
|
|
34368
|
+
modelRegistry,
|
|
34407
34369
|
thinkingLevel: opts.thinkingLevel,
|
|
34408
34370
|
temperature: opts.temperature,
|
|
34409
34371
|
topP: opts.topP,
|
|
@@ -34427,7 +34389,11 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34427
34389
|
"moltnet.task.type": task.taskType
|
|
34428
34390
|
},
|
|
34429
34391
|
sessionPersistence: executionPlan?.sessionPersistence ?? void 0,
|
|
34430
|
-
extraExtensionFactories: [
|
|
34392
|
+
extraExtensionFactories: [
|
|
34393
|
+
...runtimeParentExtensions,
|
|
34394
|
+
...toolPolicyExtensions,
|
|
34395
|
+
submitCompletion.extension
|
|
34396
|
+
]
|
|
34431
34397
|
});
|
|
34432
34398
|
} catch (err) {
|
|
34433
34399
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -34435,6 +34401,9 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
34435
34401
|
message,
|
|
34436
34402
|
phase: "session_setup"
|
|
34437
34403
|
});
|
|
34404
|
+
if (reporter.cancelSignal.aborted) return makeCancelledOutput(reporter.cancelReason ?? "Task cancelled during session setup.");
|
|
34405
|
+
if (err instanceof RuntimeProfileModelResolutionError) return makeFailedOutput("invalid_model", message);
|
|
34406
|
+
if (err instanceof GuestExecutableProbeError) return makeFailedOutput(err.code, message, finalUsage, true);
|
|
34438
34407
|
return makeFailedOutput("session_setup_failed", message);
|
|
34439
34408
|
}
|
|
34440
34409
|
const turnState = createSessionTurnState();
|
|
@@ -35268,4 +35237,4 @@ function describeToolErrorMessage(result) {
|
|
|
35268
35237
|
}
|
|
35269
35238
|
}
|
|
35270
35239
|
//#endregion
|
|
35271
|
-
export { 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, isToolVisible, loadCredentials, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveSessionToolPolicy, resolveTaskWorktreePath, resumeVm, toGuestPath };
|
|
35240
|
+
export { 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 };
|