@themoltnet/pi-runtime 0.10.1 → 0.11.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 +159 -221
- package/dist/index.js +266 -2007
- package/package.json +8 -6
package/dist/index.js
CHANGED
|
@@ -1,37 +1,23 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync
|
|
2
|
+
import { cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
|
|
3
3
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import path, { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { pipeline } from "node:stream/promises";
|
|
6
6
|
import { Type } from "@earendil-works/pi-ai";
|
|
7
7
|
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";
|
|
8
|
+
import { BrokeredHttpSecretBoundaryError, GONDOLIN_BASE_EXECUTABLES, GUEST_TASK_CONTEXT_MOUNT, GuestEnvironmentBoundaryError, activateAgentEnv, activateAgentEnv as activateAgentEnv$1, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, canonicalizeBrokeredHttpSecretDescriptor, ensureSnapshot, ensureSnapshot as ensureSnapshot$1, findMainWorktree, findMainWorktree as findMainWorktree$1, isResolvedPathInsideRoot, isResolvedPathInsideRoot as isResolvedPathInsideRoot$1, loadCredentials, prepareBrokeredHttpSecrets, resolveVfsShadowConfig, resumeVm as resumeVm$1 } from "@themoltnet/sandbox-gondolin";
|
|
8
9
|
import { SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
9
10
|
import { CID } from "multiformats/cid";
|
|
10
11
|
import * as json from "multiformats/codecs/json";
|
|
11
12
|
import { sha256 } from "multiformats/hashes/sha2";
|
|
12
|
-
import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID,
|
|
13
|
+
import { FREEFORM_TYPE, SUBMIT_OUTPUT_GATE_ID, buildTaskUserPrompt, getSubmitOutputContract, materializeTaskOutput, mergeRuntimeProfileContext, resolveTaskContext, taskTypeUsesSubagents, traceRuntimePhase, validateTaskOutput, validateTaskSubmission } from "@themoltnet/agent-runtime";
|
|
13
14
|
import { connect } from "@themoltnet/sdk/node";
|
|
14
15
|
import { ShellCommandAnalyzer } from "@themoltnet/shell-command-analyzer";
|
|
15
16
|
import { homedir } from "node:os";
|
|
16
|
-
import {
|
|
17
|
+
import { VmCheckpoint } from "@earendil-works/gondolin";
|
|
17
18
|
import { Type as Type$1 } from "typebox";
|
|
18
|
-
import { Value } from "typebox/value";
|
|
19
19
|
import { createHash } from "node:crypto";
|
|
20
|
-
import {
|
|
21
|
-
//#region src/path-containment.ts
|
|
22
|
-
/**
|
|
23
|
-
* Check containment for already-resolved lexical or real paths.
|
|
24
|
-
*
|
|
25
|
-
* Callers that accept untrusted paths must resolve/realpath at their I/O
|
|
26
|
-
* boundary first; keeping the platform-specific relative-path rule here avoids
|
|
27
|
-
* subtly different `..` and absolute-path handling across runtime cleanup,
|
|
28
|
-
* session sync, and artifact staging.
|
|
29
|
-
*/
|
|
30
|
-
function isResolvedPathInsideRoot(path, root) {
|
|
31
|
-
const rel = relative(root, path);
|
|
32
|
-
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
33
|
-
}
|
|
34
|
-
//#endregion
|
|
20
|
+
import { Value } from "typebox/value";
|
|
35
21
|
//#region src/moltnet/render-phase6.ts
|
|
36
22
|
function slugToTitle(value) {
|
|
37
23
|
return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -268,7 +254,7 @@ async function findExistingAncestor(candidate) {
|
|
|
268
254
|
}
|
|
269
255
|
}
|
|
270
256
|
function assertPathInsideWorkspace(realCwd, realPath, displayPath) {
|
|
271
|
-
if (!isResolvedPathInsideRoot(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
|
|
257
|
+
if (!isResolvedPathInsideRoot$1(realPath, realCwd)) throw new Error(`task artifact output path escapes workspace: ${displayPath}`);
|
|
272
258
|
}
|
|
273
259
|
/**
|
|
274
260
|
* Expand the `taskFilter` shorthand on the diary list/search tools into
|
|
@@ -956,11 +942,11 @@ function createMoltNetTools(config) {
|
|
|
956
942
|
defineTool({
|
|
957
943
|
name: "moltnet_host_exec",
|
|
958
944
|
label: "Run command on host (escape hatch — requires user approval)",
|
|
959
|
-
description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve — so do NOT call this tool speculatively. Routine git and gh work — pushing branches, opening pull requests, etc. — runs INSIDE the VM via the normal `bash` tool,
|
|
945
|
+
description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve — so do NOT call this tool speculatively. Routine git and gh work — pushing branches, opening pull requests, etc. — runs INSIDE the VM via the normal `bash` tool; use that, not this escape hatch. Credentials are not generally injected into the guest. A runtime may expose an opaque HTTP placeholder that the host proxy can use only for declared destinations; otherwise authenticated operations are unavailable. Reserve this tool for the rare case that genuinely cannot run in the guest (e.g. reaching a host-only resource the VM has no path to).\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass only non-secret additional vars via the `env` parameter. Every invocation is logged as an auditable host execution.",
|
|
960
946
|
parameters: Type.Object({
|
|
961
947
|
executable: Type.String({ description: "Executable to run (git | gh | moltnet)" }),
|
|
962
948
|
args: Type.Array(Type.String(), { description: "Arguments to pass to the executable" }),
|
|
963
|
-
env: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Additional environment variables for this invocation
|
|
949
|
+
env: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Additional non-secret environment variables for this invocation. Merged on top of the minimal base env." }))
|
|
964
950
|
}),
|
|
965
951
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
966
952
|
if (!HOST_EXEC_ALLOWED.has(params.executable)) throw new Error(`host_exec: '${params.executable}' is not in the allowed list (${[...HOST_EXEC_ALLOWED].join(", ")}). Extend HOST_EXEC_ALLOWED only after explicit security review.`);
|
|
@@ -1323,7 +1309,7 @@ function resolvePiCodingAgentDir() {
|
|
|
1323
1309
|
return process.env["PI_CODING_AGENT_DIR"] ?? path.join(homedir(), ".pi", "agent");
|
|
1324
1310
|
}
|
|
1325
1311
|
//#endregion
|
|
1326
|
-
//#region ../
|
|
1312
|
+
//#region ../runtime-profiles/src/context.ts
|
|
1327
1313
|
/**
|
|
1328
1314
|
* How an executor delivers a context entry to its underlying LLM.
|
|
1329
1315
|
* V1 bindings only; Tier-2 (reference_file, mcp_resource, imported_file,
|
|
@@ -1376,99 +1362,12 @@ var ContextRef = Type$1.Object({
|
|
|
1376
1362
|
additionalProperties: false
|
|
1377
1363
|
});
|
|
1378
1364
|
/** Reusable input fragment for any task type. Soft cap at 5 items. */
|
|
1379
|
-
var TaskContext
|
|
1365
|
+
var TaskContext = Type$1.Array(ContextRef, {
|
|
1380
1366
|
$id: "TaskContext",
|
|
1381
1367
|
maxItems: 5
|
|
1382
1368
|
});
|
|
1383
1369
|
//#endregion
|
|
1384
|
-
//#region ../
|
|
1385
|
-
/**
|
|
1386
|
-
* Rubric — structured acceptance criteria used by judgment tasks.
|
|
1387
|
-
*
|
|
1388
|
-
* Phase 1 (this PR): rubrics are embedded in task inputs. Their integrity
|
|
1389
|
-
* is pinned via the task's `input_cid` (which covers the whole input,
|
|
1390
|
-
* including the inline rubric). No separate storage, no CRUD.
|
|
1391
|
-
*
|
|
1392
|
-
* Phase 2 (see #881): rubrics become a first-class resource with their
|
|
1393
|
-
* own signed rows and CIDv1 lookup. The schema below is designed to
|
|
1394
|
-
* carry forward unchanged — only storage and addressing differ.
|
|
1395
|
-
*
|
|
1396
|
-
* Until Phase 2 lands, `rubricId` + `version` + `contentHash` are
|
|
1397
|
-
* informational fields the author fills in; no uniqueness is enforced.
|
|
1398
|
-
* `contentHash` is optional in Phase 1 because the *task*'s input_cid
|
|
1399
|
-
* is the authoritative commitment.
|
|
1400
|
-
*/
|
|
1401
|
-
/**
|
|
1402
|
-
* How a judge must score a single criterion.
|
|
1403
|
-
*
|
|
1404
|
-
* - `llm_score`: 0..1 continuous, `rationale` required. Smooths failures
|
|
1405
|
-
* into the gradient — use `llm_checklist` instead for properties where
|
|
1406
|
-
* a single failure is a real failure (grounding, faithfulness).
|
|
1407
|
-
* - `llm_checklist`: judge enumerates per-claim assertions with
|
|
1408
|
-
* `{passed, evidence}`. The criterion's numeric `score` is derived:
|
|
1409
|
-
* `1` iff every assertion passes, else `0`. Per-claim evidence is the
|
|
1410
|
-
* dataset for cluster-analysis of failure modes. See #999.
|
|
1411
|
-
* - `boolean`: 0 or 1, `rationale` optional.
|
|
1412
|
-
* - `deterministic_signature_check`: judge runs a signature check;
|
|
1413
|
-
* result is 0 or 1. No LLM discretion.
|
|
1414
|
-
* - `deterministic_coverage_check`: every referenced source entry
|
|
1415
|
-
* appears in the rendered output; 0 or 1.
|
|
1416
|
-
*/
|
|
1417
|
-
var RubricScoringMode = Type$1.Union([
|
|
1418
|
-
Type$1.Literal("llm_score"),
|
|
1419
|
-
Type$1.Literal("llm_checklist"),
|
|
1420
|
-
Type$1.Literal("boolean"),
|
|
1421
|
-
Type$1.Literal("deterministic_signature_check"),
|
|
1422
|
-
Type$1.Literal("deterministic_coverage_check")
|
|
1423
|
-
], { $id: "RubricScoringMode" });
|
|
1424
|
-
/**
|
|
1425
|
-
* One binary check produced by an `llm_checklist`-mode criterion.
|
|
1426
|
-
*
|
|
1427
|
-
* `evidence` is REQUIRED for both PASS and FAIL — agentskills.io grading
|
|
1428
|
-
* principle: \"Don't give the benefit of the doubt.\" A PASS without
|
|
1429
|
-
* concrete evidence (a quoted span, an entry id, a source location)
|
|
1430
|
-
* cannot be audited. A FAIL without evidence cannot be clustered into
|
|
1431
|
-
* structural fixes. The same shape is reused by `judge-eval-variant`
|
|
1432
|
-
* (#943) so tooling, dashboards, and analysis stay uniform.
|
|
1433
|
-
*/
|
|
1434
|
-
var AssertionResult = Type$1.Object({
|
|
1435
|
-
id: Type$1.String({ minLength: 1 }),
|
|
1436
|
-
text: Type$1.String({ minLength: 1 }),
|
|
1437
|
-
passed: Type$1.Boolean(),
|
|
1438
|
-
evidence: Type$1.String({ minLength: 1 })
|
|
1439
|
-
}, {
|
|
1440
|
-
$id: "AssertionResult",
|
|
1441
|
-
additionalProperties: false
|
|
1442
|
-
});
|
|
1443
|
-
var RubricCriterion = Type$1.Object({
|
|
1444
|
-
id: Type$1.String({ minLength: 1 }),
|
|
1445
|
-
description: Type$1.String({ minLength: 1 }),
|
|
1446
|
-
weight: Type$1.Number({
|
|
1447
|
-
minimum: 0,
|
|
1448
|
-
maximum: 1
|
|
1449
|
-
}),
|
|
1450
|
-
scoring: RubricScoringMode
|
|
1451
|
-
}, {
|
|
1452
|
-
$id: "RubricCriterion",
|
|
1453
|
-
additionalProperties: false
|
|
1454
|
-
});
|
|
1455
|
-
/**
|
|
1456
|
-
* A complete rubric. Same shape used in Phase 1 (inline) and Phase 2
|
|
1457
|
-
* (stored row `body`); only the addressing mechanism differs.
|
|
1458
|
-
*/
|
|
1459
|
-
var Rubric = Type$1.Object({
|
|
1460
|
-
rubricId: Type$1.String({ minLength: 1 }),
|
|
1461
|
-
version: Type$1.String({ minLength: 1 }),
|
|
1462
|
-
preamble: Type$1.Optional(Type$1.String()),
|
|
1463
|
-
criteria: Type$1.Array(RubricCriterion, { minItems: 1 }),
|
|
1464
|
-
scope: Type$1.Optional(Type$1.String()),
|
|
1465
|
-
contentHash: Type$1.Optional(Type$1.String())
|
|
1466
|
-
}, {
|
|
1467
|
-
$id: "Rubric",
|
|
1468
|
-
additionalProperties: false
|
|
1469
|
-
});
|
|
1470
|
-
//#endregion
|
|
1471
|
-
//#region ../tasks/src/runtime-models.ts
|
|
1370
|
+
//#region ../runtime-profiles/src/runtime-models.ts
|
|
1472
1371
|
/**
|
|
1473
1372
|
* Runtime model catalog: a list of supported provider/model couples that
|
|
1474
1373
|
* MoltNet daemons can target. Backed by the `runtime_models` table.
|
|
@@ -1516,7 +1415,7 @@ Type$1.Object({
|
|
|
1516
1415
|
additionalProperties: false
|
|
1517
1416
|
});
|
|
1518
1417
|
//#endregion
|
|
1519
|
-
//#region ../
|
|
1418
|
+
//#region ../runtime-profiles/src/runtime-profile-context-recipes.ts
|
|
1520
1419
|
var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
1521
1420
|
version: 1,
|
|
1522
1421
|
fragments: {
|
|
@@ -1527,12 +1426,12 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
1527
1426
|
},
|
|
1528
1427
|
"accountable-delivery-v1": {
|
|
1529
1428
|
binding: "prompt_prefix",
|
|
1530
|
-
content: "# Accountable delivery\n\n- Pair every commit made during this task with a
|
|
1429
|
+
content: "# Accountable delivery\n\n- Pair every commit made during this task with a task-provenance diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer. The tool does not currently promise a content signature; never describe an entry as signed unless the active runtime exposes and verifies a signing capability.\n- Do not disable commit signing when the active runtime provides it. A host-authenticated guest has no injected signing key, so do not recover one from host configuration or claim an unsigned commit is signed.\n- Push a branch and open or update a pull request only when the task asks for it. Use a host-brokered GitHub placeholder only when the runtime kernel declares one; if no GitHub credential is active, the authenticated operation is unavailable.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
|
|
1531
1430
|
slug: "accountable-delivery-v1"
|
|
1532
1431
|
},
|
|
1533
1432
|
"judgment-diary-v1": {
|
|
1534
1433
|
binding: "prompt_prefix",
|
|
1535
|
-
content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a
|
|
1434
|
+
content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a diary entry with the `moltnet_create_entry` custom tool before submitting the structured judgment. Capture the rationale and evidence that support the verdict. Do not claim a content signature unless the active runtime exposes and verifies a signing capability.\n- Add the `judgment` tag and the active task type tag (`assess_brief`, `judge_pack`, or `pr_review`). For `judge_pack`, also add `rubric:<rubricId>` from the task facts.\n- Do not use a shell `moltnet entry` command: task provenance is injected only by the custom tool.",
|
|
1536
1435
|
slug: "judgment-diary-v1"
|
|
1537
1436
|
},
|
|
1538
1437
|
"proactive-memory-v1": {
|
|
@@ -2532,7 +2431,7 @@ var toolEnforcementLiterals = [
|
|
|
2532
2431
|
];
|
|
2533
2432
|
var ToolEnforcementSchema = Type$1.Union(toolEnforcementLiterals, { description: "Runtime tool-policy enforcement mode: off (inert), watch (audit only), enforce (block disallowed tools, fail-closed)." });
|
|
2534
2433
|
//#endregion
|
|
2535
|
-
//#region ../
|
|
2434
|
+
//#region ../runtime-profiles/src/runtime-profiles.ts
|
|
2536
2435
|
var RuntimeProfileName = Type$1.String({
|
|
2537
2436
|
minLength: 1,
|
|
2538
2437
|
maxLength: 100,
|
|
@@ -2651,7 +2550,7 @@ var RuntimeProfileContext = Type$1.Object({
|
|
|
2651
2550
|
$id: "RuntimeProfileContext",
|
|
2652
2551
|
additionalProperties: false
|
|
2653
2552
|
});
|
|
2654
|
-
|
|
2553
|
+
Type$1.Object({ profileId: Type$1.String({ format: "uuid" }) }, {
|
|
2655
2554
|
$id: "RuntimeProfileRef",
|
|
2656
2555
|
additionalProperties: false
|
|
2657
2556
|
});
|
|
@@ -2731,7 +2630,7 @@ Type$1.Object({
|
|
|
2731
2630
|
additionalProperties: false
|
|
2732
2631
|
});
|
|
2733
2632
|
//#endregion
|
|
2734
|
-
//#region ../
|
|
2633
|
+
//#region ../runtime-profiles/src/runtime-sessions.ts
|
|
2735
2634
|
var RuntimeSessionKind = Type$1.Union([
|
|
2736
2635
|
Type$1.Literal("root"),
|
|
2737
2636
|
Type$1.Literal("extend"),
|
|
@@ -2789,7 +2688,7 @@ Type$1.Object({
|
|
|
2789
2688
|
additionalProperties: false
|
|
2790
2689
|
});
|
|
2791
2690
|
//#endregion
|
|
2792
|
-
//#region ../
|
|
2691
|
+
//#region ../runtime-profiles/src/runtime-slots.ts
|
|
2793
2692
|
var RuntimeWorkspaceKind = Type$1.Union([
|
|
2794
2693
|
Type$1.Literal("origin"),
|
|
2795
2694
|
Type$1.Literal("fork"),
|
|
@@ -2918,1248 +2817,37 @@ Type$1.Object({
|
|
|
2918
2817
|
additionalProperties: false
|
|
2919
2818
|
});
|
|
2920
2819
|
//#endregion
|
|
2921
|
-
//#region
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
* verbatim from `./rubric.ts`.
|
|
2940
|
-
* - `sideEffects` Required process side-effects (e.g. diary entry)
|
|
2941
|
-
*
|
|
2942
|
-
* ## Two roles, two task types
|
|
2943
|
-
*
|
|
2944
|
-
* **Producer self-assessment** (fulfillment tasks: `fulfill_brief`,
|
|
2945
|
-
* `curate_pack`, `render_pack`). The producer **LLM** evaluates the
|
|
2946
|
-
* criteria against its own output and emits a `VerificationRecord`
|
|
2947
|
-
* inside `output.verification`. The daemon is pure passthrough — it
|
|
2948
|
-
* does not run `evaluateAssertions`, does not inspect the verification
|
|
2949
|
-
* record. The REST API is dumb storage; it never re-runs assertions and
|
|
2950
|
-
* never runs LLMs. The cross-field rule
|
|
2951
|
-
* `requireVerificationWhenCriteriaPresent` enforces "verification
|
|
2952
|
-
* required iff successCriteria present" at task-output validation time
|
|
2953
|
-
* (server-side schema check). Self-assessment is a truthful self-rating,
|
|
2954
|
-
* NOT enforcement — `verification.passed=false` does not block /complete
|
|
2955
|
-
* and does not affect `acceptedAttemptN`. See
|
|
2956
|
-
* `docs/use/tasks-and-runtime.md` for the full producer/judge flow.
|
|
2957
|
-
*
|
|
2958
|
-
* **Binding evaluation** (judgment tasks: `assess_brief`, `judge_pack`).
|
|
2959
|
-
* A separate task whose IS the application of `successCriteria` to
|
|
2960
|
-
* someone else's output. Different agent (enforced at claim time), same
|
|
2961
|
-
* envelope. The judge's verdict is binding: this is the *gate* in the
|
|
2962
|
-
* MoltNet model. The rubric inside `successCriteria.rubric` IS the job
|
|
2963
|
-
* spec for the judge.
|
|
2964
|
-
*
|
|
2965
|
-
* The clean chain: producer task with `successCriteria` → producer
|
|
2966
|
-
* self-assesses honestly → proposer (or automation) creates a downstream
|
|
2967
|
-
* judgment task that references the same `successCriteria` (or a
|
|
2968
|
-
* stricter rubric) → judgment task delivers the binding verdict.
|
|
2969
|
-
*
|
|
2970
|
-
* Storage: SuccessCriteria lives inline at `task.input.successCriteria`,
|
|
2971
|
-
* pinned via the task's `inputCid`. No separate column or hash. When
|
|
2972
|
-
* #881 lands, the `rubric` field can graduate to `{ rubricCid }` lookup
|
|
2973
|
-
* without changing this envelope, and producer + judge tasks can pin
|
|
2974
|
-
* the SAME rubric across the chain for end-to-end auditability.
|
|
2975
|
-
*/
|
|
2976
|
-
var SchemaCheckSpec = Type$1.Object({ schemaCid: Type$1.String({ minLength: 1 }) }, { additionalProperties: false });
|
|
2977
|
-
var CidEqualsSpec = Type$1.Object({
|
|
2978
|
-
path: Type$1.String({ minLength: 1 }),
|
|
2979
|
-
expected: Type$1.String({ minLength: 1 })
|
|
2980
|
-
}, { additionalProperties: false });
|
|
2981
|
-
var SubmitToolCallGate = Type$1.Object({
|
|
2982
|
-
id: Type$1.String({ minLength: 1 }),
|
|
2983
|
-
kind: Type$1.Literal("submit-tool-call"),
|
|
2984
|
-
description: Type$1.String({ minLength: 1 }),
|
|
2985
|
-
required: Type$1.Boolean()
|
|
2986
|
-
}, { additionalProperties: false });
|
|
2987
|
-
var Gate = Type$1.Union([
|
|
2988
|
-
SubmitToolCallGate,
|
|
2989
|
-
Type$1.Object({
|
|
2990
|
-
id: Type$1.String({ minLength: 1 }),
|
|
2991
|
-
kind: Type$1.Literal("schema-check"),
|
|
2992
|
-
spec: SchemaCheckSpec,
|
|
2993
|
-
required: Type$1.Boolean()
|
|
2994
|
-
}, { additionalProperties: false }),
|
|
2995
|
-
Type$1.Object({
|
|
2996
|
-
id: Type$1.String({ minLength: 1 }),
|
|
2997
|
-
kind: Type$1.Literal("cid-equals"),
|
|
2998
|
-
spec: CidEqualsSpec,
|
|
2999
|
-
required: Type$1.Boolean()
|
|
3000
|
-
}, { additionalProperties: false })
|
|
3001
|
-
], { $id: "Gate" });
|
|
3002
|
-
var AssertionOp = Type$1.Union([
|
|
3003
|
-
Type$1.Literal("exists"),
|
|
3004
|
-
Type$1.Literal("equals"),
|
|
3005
|
-
Type$1.Literal("matches"),
|
|
3006
|
-
Type$1.Literal("in-range"),
|
|
3007
|
-
Type$1.Literal("min-length")
|
|
3008
|
-
], { $id: "AssertionOp" });
|
|
3009
|
-
var Assertion = Type$1.Object({
|
|
3010
|
-
id: Type$1.String({ minLength: 1 }),
|
|
3011
|
-
path: Type$1.String({ minLength: 1 }),
|
|
3012
|
-
op: AssertionOp,
|
|
3013
|
-
value: Type$1.Optional(Type$1.Unknown())
|
|
3014
|
-
}, {
|
|
3015
|
-
$id: "Assertion",
|
|
3016
|
-
additionalProperties: false
|
|
3017
|
-
});
|
|
3018
|
-
var SideEffectsSpec = Type$1.Object({
|
|
3019
|
-
diaryEntryRequired: Type$1.Optional(Type$1.Boolean()),
|
|
3020
|
-
diaryEntryTags: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 }))),
|
|
3021
|
-
referencedEntries: Type$1.Optional(Type$1.Integer({ minimum: 0 }))
|
|
3022
|
-
}, {
|
|
3023
|
-
$id: "SideEffectsSpec",
|
|
3024
|
-
additionalProperties: false
|
|
3025
|
-
});
|
|
3026
|
-
var SuccessCriteria = Type$1.Object({
|
|
3027
|
-
version: Type$1.Literal(1),
|
|
3028
|
-
gates: Type$1.Optional(Type$1.Array(Gate)),
|
|
3029
|
-
assertions: Type$1.Optional(Type$1.Array(Assertion)),
|
|
3030
|
-
rubric: Type$1.Optional(Rubric),
|
|
3031
|
-
minComposite: Type$1.Optional(Type$1.Number({
|
|
3032
|
-
minimum: 0,
|
|
3033
|
-
maximum: 1
|
|
3034
|
-
})),
|
|
3035
|
-
sideEffects: Type$1.Optional(SideEffectsSpec)
|
|
3036
|
-
}, {
|
|
3037
|
-
$id: "SuccessCriteria",
|
|
3038
|
-
additionalProperties: false
|
|
3039
|
-
});
|
|
3040
|
-
var VerificationResultStatus = Type$1.Union([
|
|
3041
|
-
Type$1.Literal("pass"),
|
|
3042
|
-
Type$1.Literal("fail"),
|
|
3043
|
-
Type$1.Literal("skip")
|
|
3044
|
-
], { $id: "VerificationResultStatus" });
|
|
3045
|
-
var VerificationResultKind = Type$1.Union([
|
|
3046
|
-
Type$1.Literal("gate"),
|
|
3047
|
-
Type$1.Literal("assertion"),
|
|
3048
|
-
Type$1.Literal("rubric"),
|
|
3049
|
-
Type$1.Literal("sideEffect")
|
|
3050
|
-
], { $id: "VerificationResultKind" });
|
|
3051
|
-
var VerificationResult = Type$1.Object({
|
|
3052
|
-
id: Type$1.String({ minLength: 1 }),
|
|
3053
|
-
kind: VerificationResultKind,
|
|
3054
|
-
status: VerificationResultStatus,
|
|
3055
|
-
detail: Type$1.Optional(Type$1.String())
|
|
3056
|
-
}, {
|
|
3057
|
-
$id: "VerificationResult",
|
|
3058
|
-
additionalProperties: false
|
|
3059
|
-
});
|
|
3060
|
-
var VerificationRecord = Type$1.Object({
|
|
3061
|
-
inputCid: Type$1.String({ minLength: 1 }),
|
|
3062
|
-
results: Type$1.Array(VerificationResult),
|
|
3063
|
-
passed: Type$1.Boolean({ description: "True iff every verification result has status \"pass\" or \"skip\"; false when any result has status \"fail\"." })
|
|
3064
|
-
}, {
|
|
3065
|
-
$id: "VerificationRecord",
|
|
3066
|
-
additionalProperties: false
|
|
3067
|
-
});
|
|
3068
|
-
//#endregion
|
|
3069
|
-
//#region ../tasks/src/task-artifacts.ts
|
|
3070
|
-
var TaskArtifact = Type$1.Object({
|
|
3071
|
-
id: Type$1.String({ format: "uuid" }),
|
|
3072
|
-
teamId: Type$1.String({ format: "uuid" }),
|
|
3073
|
-
taskId: Type$1.String({ format: "uuid" }),
|
|
3074
|
-
attemptN: Type$1.Union([Type$1.Integer({ minimum: 1 }), Type$1.Null()]),
|
|
3075
|
-
kind: Type$1.String({
|
|
3076
|
-
minLength: 1,
|
|
3077
|
-
maxLength: 100
|
|
3078
|
-
}),
|
|
3079
|
-
title: Type$1.String({
|
|
3080
|
-
minLength: 1,
|
|
3081
|
-
maxLength: 255
|
|
3082
|
-
}),
|
|
3083
|
-
contentType: Type$1.String({
|
|
3084
|
-
minLength: 1,
|
|
3085
|
-
maxLength: 200
|
|
3086
|
-
}),
|
|
3087
|
-
contentEncoding: Type$1.Union([Type$1.String({
|
|
3088
|
-
minLength: 1,
|
|
3089
|
-
maxLength: 100
|
|
3090
|
-
}), Type$1.Null()]),
|
|
3091
|
-
sizeBytes: Type$1.Integer({ minimum: 0 }),
|
|
3092
|
-
cid: Type$1.String({
|
|
3093
|
-
minLength: 1,
|
|
3094
|
-
maxLength: 100
|
|
3095
|
-
}),
|
|
3096
|
-
createdByAgentId: Type$1.Union([Type$1.String({ format: "uuid" }), Type$1.Null()]),
|
|
3097
|
-
expiresAt: Type$1.Union([Type$1.String({ format: "date-time" }), Type$1.Null()]),
|
|
3098
|
-
createdAt: Type$1.String({ format: "date-time" })
|
|
3099
|
-
}, { $id: "TaskArtifact" });
|
|
3100
|
-
Type$1.Object({
|
|
3101
|
-
artifacts: Type$1.Array(TaskArtifact),
|
|
3102
|
-
nextCursor: Type$1.Union([Type$1.String({ minLength: 1 }), Type$1.Null()])
|
|
3103
|
-
}, { $id: "TaskArtifactList" });
|
|
3104
|
-
Type$1.Object({
|
|
3105
|
-
limit: Type$1.Optional(Type$1.Integer({
|
|
3106
|
-
minimum: 1,
|
|
3107
|
-
maximum: 100
|
|
3108
|
-
})),
|
|
3109
|
-
cursor: Type$1.Optional(Type$1.String({ minLength: 1 }))
|
|
3110
|
-
}, {
|
|
3111
|
-
$id: "ListTaskArtifactsQuery",
|
|
3112
|
-
additionalProperties: false
|
|
3113
|
-
});
|
|
3114
|
-
var HeaderSafeContentType = Type$1.String({
|
|
3115
|
-
minLength: 1,
|
|
3116
|
-
maxLength: 200,
|
|
3117
|
-
pattern: "^[\\x21-\\x7e][\\x20-\\x7e]*$"
|
|
3118
|
-
});
|
|
3119
|
-
var HeaderSafeContentEncoding = Type$1.String({
|
|
3120
|
-
minLength: 1,
|
|
3121
|
-
maxLength: 100,
|
|
3122
|
-
pattern: "^[\\x21-\\x7e][\\x20-\\x7e]*$"
|
|
3123
|
-
});
|
|
3124
|
-
Type$1.Object({
|
|
3125
|
-
kind: Type$1.String({
|
|
3126
|
-
minLength: 1,
|
|
3127
|
-
maxLength: 100
|
|
3128
|
-
}),
|
|
3129
|
-
title: Type$1.String({
|
|
3130
|
-
minLength: 1,
|
|
3131
|
-
maxLength: 255
|
|
3132
|
-
}),
|
|
3133
|
-
contentType: Type$1.Optional(HeaderSafeContentType),
|
|
3134
|
-
contentEncoding: Type$1.Optional(HeaderSafeContentEncoding)
|
|
3135
|
-
}, {
|
|
3136
|
-
$id: "UploadTaskArtifactQuery",
|
|
3137
|
-
additionalProperties: false
|
|
3138
|
-
});
|
|
3139
|
-
Type$1.String({
|
|
3140
|
-
$id: "TaskArtifactContent",
|
|
3141
|
-
description: "Task artifact content stream.",
|
|
3142
|
-
format: "binary"
|
|
3143
|
-
});
|
|
3144
|
-
Type$1.Object({ taskId: Type$1.String({ format: "uuid" }) }, {
|
|
3145
|
-
$id: "TaskArtifactTaskParams",
|
|
3146
|
-
additionalProperties: false
|
|
3147
|
-
});
|
|
3148
|
-
Type$1.Object({
|
|
3149
|
-
taskId: Type$1.String({ format: "uuid" }),
|
|
3150
|
-
attemptN: Type$1.Integer({ minimum: 1 })
|
|
3151
|
-
}, {
|
|
3152
|
-
$id: "TaskArtifactAttemptParams",
|
|
3153
|
-
additionalProperties: false
|
|
3154
|
-
});
|
|
3155
|
-
Type$1.Object({
|
|
3156
|
-
taskId: Type$1.String({ format: "uuid" }),
|
|
3157
|
-
attemptN: Type$1.Integer({ minimum: 1 }),
|
|
3158
|
-
cid: Type$1.String({
|
|
3159
|
-
minLength: 1,
|
|
3160
|
-
maxLength: 100
|
|
3161
|
-
})
|
|
3162
|
-
}, {
|
|
3163
|
-
$id: "TaskArtifactContentParams",
|
|
3164
|
-
additionalProperties: false
|
|
3165
|
-
});
|
|
3166
|
-
Type$1.Object({
|
|
3167
|
-
contentType: Type$1.Optional(HeaderSafeContentType),
|
|
3168
|
-
contentEncoding: Type$1.Optional(HeaderSafeContentEncoding)
|
|
3169
|
-
}, {
|
|
3170
|
-
$id: "StageTaskArtifactQuery",
|
|
3171
|
-
additionalProperties: false
|
|
3172
|
-
});
|
|
3173
|
-
Type$1.Object({
|
|
3174
|
-
cid: Type$1.String({
|
|
3175
|
-
minLength: 1,
|
|
3176
|
-
maxLength: 100
|
|
3177
|
-
}),
|
|
3178
|
-
sizeBytes: Type$1.Integer({ minimum: 0 }),
|
|
3179
|
-
contentType: Type$1.String({
|
|
3180
|
-
minLength: 1,
|
|
3181
|
-
maxLength: 200
|
|
3182
|
-
})
|
|
3183
|
-
}, { $id: "StagedTaskArtifact" });
|
|
3184
|
-
Type$1.Object({
|
|
3185
|
-
taskId: Type$1.String({ format: "uuid" }),
|
|
3186
|
-
cid: Type$1.String({
|
|
3187
|
-
minLength: 1,
|
|
3188
|
-
maxLength: 100
|
|
3189
|
-
})
|
|
3190
|
-
}, {
|
|
3191
|
-
$id: "TaskArtifactTaskContentParams",
|
|
3192
|
-
additionalProperties: false
|
|
3193
|
-
});
|
|
3194
|
-
Type$1.Object({
|
|
3195
|
-
targetTaskId: Type$1.String({ format: "uuid" }),
|
|
3196
|
-
successCriteria: SuccessCriteria
|
|
3197
|
-
}, {
|
|
3198
|
-
$id: "AssessBriefInput",
|
|
3199
|
-
additionalProperties: false
|
|
3200
|
-
});
|
|
3201
|
-
/** One score line. */
|
|
3202
|
-
var AssessBriefScore = Type$1.Object({
|
|
3203
|
-
criterionId: Type$1.String({ minLength: 1 }),
|
|
3204
|
-
score: Type$1.Number({
|
|
3205
|
-
minimum: 0,
|
|
3206
|
-
maximum: 1
|
|
3207
|
-
}),
|
|
3208
|
-
rationale: Type$1.Optional(Type$1.String()),
|
|
3209
|
-
evidence: Type$1.Optional(Type$1.Object({
|
|
3210
|
-
commitsVerified: Type$1.Number(),
|
|
3211
|
-
commitsTotal: Type$1.Number(),
|
|
3212
|
-
signatureFailures: Type$1.Array(Type$1.String())
|
|
3213
|
-
}, { additionalProperties: false }))
|
|
3214
|
-
}, {
|
|
3215
|
-
$id: "AssessBriefScore",
|
|
3216
|
-
additionalProperties: false
|
|
3217
|
-
});
|
|
3218
|
-
Type$1.Object({
|
|
3219
|
-
scores: Type$1.Array(AssessBriefScore, { minItems: 1 }),
|
|
3220
|
-
composite: Type$1.Number({
|
|
3221
|
-
minimum: 0,
|
|
3222
|
-
maximum: 1
|
|
3223
|
-
}),
|
|
3224
|
-
verdict: Type$1.String({ minLength: 1 }),
|
|
3225
|
-
judgeModel: Type$1.Optional(Type$1.String())
|
|
3226
|
-
}, {
|
|
3227
|
-
$id: "AssessBriefOutput",
|
|
3228
|
-
additionalProperties: false
|
|
3229
|
-
});
|
|
3230
|
-
//#endregion
|
|
3231
|
-
//#region ../tasks/src/task-types/curate-pack.ts
|
|
2820
|
+
//#region src/runtime-definition.ts
|
|
2821
|
+
var PI_RUNTIME_DEFINITION_VERSION = "moltnet:pi-runtime:v1";
|
|
2822
|
+
var PI_EXECUTOR_MANIFEST_VERSION = "moltnet:executor-manifest:v1";
|
|
2823
|
+
var DEFAULT_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS = 3e4;
|
|
2824
|
+
var MAX_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS = 2147483647;
|
|
2825
|
+
var PiBrokeredHttpSecretResolutionError = class extends Error {
|
|
2826
|
+
constructor(requirementId, message, retryable) {
|
|
2827
|
+
super(message);
|
|
2828
|
+
this.requirementId = requirementId;
|
|
2829
|
+
this.retryable = retryable;
|
|
2830
|
+
this.name = "PiBrokeredHttpSecretResolutionError";
|
|
2831
|
+
}
|
|
2832
|
+
};
|
|
2833
|
+
function brokeredHttpSecretAbortError() {
|
|
2834
|
+
const error = /* @__PURE__ */ new Error("Brokered HTTP secret resolution aborted");
|
|
2835
|
+
error.name = "AbortError";
|
|
2836
|
+
return error;
|
|
2837
|
+
}
|
|
3232
2838
|
/**
|
|
3233
|
-
*
|
|
3234
|
-
*
|
|
3235
|
-
*
|
|
3236
|
-
* criteria: not required (rubric-less curation recipe)
|
|
3237
|
-
* references: optional (e.g. a prior rendered pack being re-curated)
|
|
3238
|
-
*
|
|
3239
|
-
* This is step 1 of the three-session attribution loop (#875). The agent
|
|
3240
|
-
* runs a structured exploration over a diary — tag inventory, hybrid
|
|
3241
|
-
* search, type/tag narrowing — and emits a ranked entry list via
|
|
3242
|
-
* `moltnet_pack_create`. The prompt is deterministic given the input
|
|
3243
|
-
* (no operator interaction), so two runs with the same input should
|
|
3244
|
-
* converge on similar packs.
|
|
3245
|
-
*
|
|
3246
|
-
* Related: `render_pack`, `judge_pack`.
|
|
2839
|
+
* Declare a value-free HTTP credential requirement in trusted runtime code.
|
|
2840
|
+
* The resolver runs per attempt on the daemon host; its return value is never
|
|
2841
|
+
* added to the runtime definition or executor manifest.
|
|
3247
2842
|
*/
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
Type$1.Object({
|
|
3255
|
-
diaryId: Type$1.String({ format: "uuid" }),
|
|
3256
|
-
taskPrompt: Type$1.String({ minLength: 1 }),
|
|
3257
|
-
entryTypes: Type$1.Optional(Type$1.Array(EntryTypeFilter, { minItems: 1 })),
|
|
3258
|
-
tagFilters: Type$1.Optional(Type$1.Object({
|
|
3259
|
-
include: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
3260
|
-
exclude: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
3261
|
-
prefix: Type$1.Optional(Type$1.String())
|
|
3262
|
-
}, { additionalProperties: false })),
|
|
3263
|
-
tokenBudget: Type$1.Optional(Type$1.Number({ minimum: 500 })),
|
|
3264
|
-
recipe: Type$1.Optional(Type$1.Union([Type$1.Literal("topic-focused-v1"), Type$1.Literal("scope-inventory-v1")])),
|
|
3265
|
-
successCriteria: Type$1.Optional(SuccessCriteria)
|
|
3266
|
-
}, {
|
|
3267
|
-
$id: "CuratePackInput",
|
|
3268
|
-
additionalProperties: false
|
|
3269
|
-
});
|
|
3270
|
-
Type$1.Object({
|
|
3271
|
-
packId: Type$1.String({ format: "uuid" }),
|
|
3272
|
-
packCid: Type$1.String({ minLength: 1 }),
|
|
3273
|
-
entries: Type$1.Array(Type$1.Object({
|
|
3274
|
-
entryId: Type$1.String({ format: "uuid" }),
|
|
3275
|
-
rank: Type$1.Number({ minimum: 1 }),
|
|
3276
|
-
rationale: Type$1.String({ minLength: 1 })
|
|
3277
|
-
}, { additionalProperties: false }), { minItems: 1 }),
|
|
3278
|
-
recipeParams: Type$1.Record(Type$1.String(), Type$1.Unknown()),
|
|
3279
|
-
checkpoints: Type$1.Optional(Type$1.Array(Type$1.Object({
|
|
3280
|
-
phase: Type$1.String({ minLength: 1 }),
|
|
3281
|
-
candidateIds: Type$1.Array(Type$1.String({ format: "uuid" })),
|
|
3282
|
-
droppedIds: Type$1.Optional(Type$1.Array(Type$1.String({ format: "uuid" }))),
|
|
3283
|
-
notes: Type$1.String({ minLength: 1 })
|
|
3284
|
-
}, { additionalProperties: false }))),
|
|
3285
|
-
summary: Type$1.String({ minLength: 1 }),
|
|
3286
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3287
|
-
}, {
|
|
3288
|
-
$id: "CuratePackOutput",
|
|
3289
|
-
additionalProperties: false
|
|
3290
|
-
});
|
|
3291
|
-
//#endregion
|
|
3292
|
-
//#region ../tasks/src/task-types/freeform.ts
|
|
3293
|
-
var FreeformExecutionOptions = Type$1.Object({
|
|
3294
|
-
workspace: Type$1.Optional(Type$1.Union([
|
|
3295
|
-
Type$1.Literal("none"),
|
|
3296
|
-
Type$1.Literal("shared_mount"),
|
|
3297
|
-
Type$1.Literal("dedicated_worktree")
|
|
3298
|
-
])),
|
|
3299
|
-
revision: Type$1.Optional(Type$1.String({ pattern: "^[0-9a-fA-F]{40}$" }))
|
|
3300
|
-
}, {
|
|
3301
|
-
$id: "FreeformExecutionOptions",
|
|
3302
|
-
additionalProperties: false
|
|
3303
|
-
});
|
|
3304
|
-
var FreeformContinueFrom = Type$1.Object({
|
|
3305
|
-
taskId: Type$1.String({ format: "uuid" }),
|
|
3306
|
-
attemptN: Type$1.Integer({ minimum: 1 }),
|
|
3307
|
-
mode: Type$1.Optional(Type$1.Union([Type$1.Literal("extend"), Type$1.Literal("fork")]))
|
|
3308
|
-
}, {
|
|
3309
|
-
$id: "FreeformContinueFrom",
|
|
3310
|
-
additionalProperties: false
|
|
3311
|
-
});
|
|
3312
|
-
var FreeformTaskTypeProposal = Type$1.Object({
|
|
3313
|
-
name: Type$1.String({ minLength: 1 }),
|
|
3314
|
-
rationale: Type$1.String({ minLength: 1 }),
|
|
3315
|
-
inputShape: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown())),
|
|
3316
|
-
outputShape: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown()))
|
|
3317
|
-
}, {
|
|
3318
|
-
$id: "FreeformTaskTypeProposal",
|
|
3319
|
-
additionalProperties: false
|
|
3320
|
-
});
|
|
3321
|
-
Type$1.Object({
|
|
3322
|
-
brief: Type$1.String({ minLength: 1 }),
|
|
3323
|
-
expectedOutput: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3324
|
-
constraints: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 }), { maxItems: 20 })),
|
|
3325
|
-
suggestedTaskType: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3326
|
-
successCriteria: Type$1.Optional(SuccessCriteria),
|
|
3327
|
-
context: Type$1.Optional(TaskContext$1),
|
|
3328
|
-
execution: Type$1.Optional(FreeformExecutionOptions),
|
|
3329
|
-
continueFrom: Type$1.Optional(FreeformContinueFrom)
|
|
3330
|
-
}, {
|
|
3331
|
-
$id: "FreeformInput",
|
|
3332
|
-
additionalProperties: false
|
|
3333
|
-
});
|
|
3334
|
-
var FreeformArtifact = Type$1.Object({
|
|
3335
|
-
kind: Type$1.String({ minLength: 1 }),
|
|
3336
|
-
title: Type$1.String({ minLength: 1 }),
|
|
3337
|
-
description: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3338
|
-
url: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3339
|
-
path: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3340
|
-
cid: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3341
|
-
contentType: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3342
|
-
contentEncoding: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3343
|
-
sizeBytes: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
3344
|
-
body: Type$1.Optional(Type$1.String({ maxLength: 65536 }))
|
|
3345
|
-
}, {
|
|
3346
|
-
$id: "FreeformArtifact",
|
|
3347
|
-
additionalProperties: false
|
|
3348
|
-
});
|
|
3349
|
-
Type$1.Object({
|
|
3350
|
-
summary: Type$1.String({ minLength: 1 }),
|
|
3351
|
-
branch: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3352
|
-
artifacts: Type$1.Optional(Type$1.Array(FreeformArtifact, { maxItems: 20 })),
|
|
3353
|
-
proposedTaskType: Type$1.Optional(FreeformTaskTypeProposal),
|
|
3354
|
-
diaryEntryIds: Type$1.Optional(Type$1.Array(Type$1.String({ format: "uuid" }))),
|
|
3355
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3356
|
-
}, {
|
|
3357
|
-
$id: "FreeformOutput",
|
|
3358
|
-
additionalProperties: false
|
|
3359
|
-
});
|
|
3360
|
-
Type$1.Object({
|
|
3361
|
-
brief: Type$1.String({ minLength: 1 }),
|
|
3362
|
-
successCriteria: Type$1.Optional(SuccessCriteria),
|
|
3363
|
-
seedFiles: Type$1.Optional(Type$1.Array(Type$1.String())),
|
|
3364
|
-
scopeHint: Type$1.Optional(Type$1.String())
|
|
3365
|
-
}, {
|
|
3366
|
-
$id: "FulfillBriefInput",
|
|
3367
|
-
additionalProperties: false
|
|
3368
|
-
});
|
|
3369
|
-
Type$1.Object({
|
|
3370
|
-
branch: Type$1.String({ minLength: 1 }),
|
|
3371
|
-
commits: Type$1.Array(Type$1.Object({
|
|
3372
|
-
sha: Type$1.String({ minLength: 7 }),
|
|
3373
|
-
message: Type$1.String(),
|
|
3374
|
-
diaryEntryId: Type$1.Union([Type$1.String({ format: "uuid" }), Type$1.Null()])
|
|
3375
|
-
}, { additionalProperties: false })),
|
|
3376
|
-
pullRequestUrl: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
3377
|
-
diaryEntryIds: Type$1.Array(Type$1.String({ format: "uuid" })),
|
|
3378
|
-
summary: Type$1.String({ minLength: 1 }),
|
|
3379
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3380
|
-
}, {
|
|
3381
|
-
$id: "FulfillBriefOutput",
|
|
3382
|
-
additionalProperties: false
|
|
3383
|
-
});
|
|
3384
|
-
Type$1.Object({
|
|
3385
|
-
renderedPackId: Type$1.String({ format: "uuid" }),
|
|
3386
|
-
sourcePackId: Type$1.String({ format: "uuid" }),
|
|
3387
|
-
successCriteria: SuccessCriteria
|
|
3388
|
-
}, {
|
|
3389
|
-
$id: "JudgePackInput",
|
|
3390
|
-
additionalProperties: false
|
|
3391
|
-
});
|
|
3392
|
-
/** One scored criterion. Mirrors `AssessBriefScore`. */
|
|
3393
|
-
var JudgePackScore = Type$1.Object({
|
|
3394
|
-
criterionId: Type$1.String({ minLength: 1 }),
|
|
3395
|
-
score: Type$1.Number({
|
|
3396
|
-
minimum: 0,
|
|
3397
|
-
maximum: 1
|
|
3398
|
-
}),
|
|
3399
|
-
rationale: Type$1.Optional(Type$1.String()),
|
|
3400
|
-
assertions: Type$1.Optional(Type$1.Array(AssertionResult, { minItems: 1 })),
|
|
3401
|
-
evidence: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown()))
|
|
3402
|
-
}, {
|
|
3403
|
-
$id: "JudgePackScore",
|
|
3404
|
-
additionalProperties: false
|
|
3405
|
-
});
|
|
3406
|
-
Type$1.Object({
|
|
3407
|
-
scores: Type$1.Array(JudgePackScore, { minItems: 1 }),
|
|
3408
|
-
composite: Type$1.Number({
|
|
3409
|
-
minimum: 0,
|
|
3410
|
-
maximum: 1
|
|
3411
|
-
}),
|
|
3412
|
-
verdict: Type$1.String({ minLength: 1 }),
|
|
3413
|
-
judgeModel: Type$1.Optional(Type$1.String()),
|
|
3414
|
-
rendererBinaryCid: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()]))
|
|
3415
|
-
}, {
|
|
3416
|
-
$id: "JudgePackOutput",
|
|
3417
|
-
additionalProperties: false
|
|
3418
|
-
});
|
|
3419
|
-
Type$1.Object({
|
|
3420
|
-
targetTaskId: Type$1.String({ format: "uuid" }),
|
|
3421
|
-
targetAttemptN: Type$1.Integer({ minimum: 1 }),
|
|
3422
|
-
successCriteria: SuccessCriteria
|
|
3423
|
-
}, {
|
|
3424
|
-
$id: "JudgeEvalAttemptInput",
|
|
3425
|
-
additionalProperties: false
|
|
3426
|
-
});
|
|
3427
|
-
Type$1.Object({
|
|
3428
|
-
targetTaskId: Type$1.String({ format: "uuid" }),
|
|
3429
|
-
targetAttemptN: Type$1.Integer({ minimum: 1 }),
|
|
3430
|
-
variantLabel: Type$1.String({
|
|
3431
|
-
minLength: 1,
|
|
3432
|
-
maxLength: 64,
|
|
3433
|
-
pattern: "^(?!.* - ).*$"
|
|
3434
|
-
}),
|
|
3435
|
-
scores: Type$1.Array(JudgePackScore, { minItems: 1 }),
|
|
3436
|
-
composite: Type$1.Number({
|
|
3437
|
-
minimum: 0,
|
|
3438
|
-
maximum: 1
|
|
3439
|
-
}),
|
|
3440
|
-
verdict: Type$1.String({ minLength: 1 }),
|
|
3441
|
-
judgeModel: Type$1.Optional(Type$1.String({ minLength: 1 }))
|
|
3442
|
-
}, {
|
|
3443
|
-
$id: "JudgeEvalAttemptSubmission",
|
|
3444
|
-
additionalProperties: false
|
|
3445
|
-
});
|
|
3446
|
-
Type$1.Object({
|
|
3447
|
-
targetTaskId: Type$1.String({ format: "uuid" }),
|
|
3448
|
-
targetAttemptN: Type$1.Integer({ minimum: 1 }),
|
|
3449
|
-
variantLabel: Type$1.String({
|
|
3450
|
-
minLength: 1,
|
|
3451
|
-
maxLength: 64,
|
|
3452
|
-
pattern: "^(?!.* - ).*$"
|
|
3453
|
-
}),
|
|
3454
|
-
scores: Type$1.Array(JudgePackScore, { minItems: 1 }),
|
|
3455
|
-
composite: Type$1.Number({
|
|
3456
|
-
minimum: 0,
|
|
3457
|
-
maximum: 1
|
|
3458
|
-
}),
|
|
3459
|
-
verdict: Type$1.String({ minLength: 1 }),
|
|
3460
|
-
judgeModel: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3461
|
-
traceparent: Type$1.Optional(Type$1.String({ minLength: 1 }))
|
|
3462
|
-
}, {
|
|
3463
|
-
$id: "JudgeEvalAttemptOutput",
|
|
3464
|
-
additionalProperties: false
|
|
3465
|
-
});
|
|
3466
|
-
//#endregion
|
|
3467
|
-
//#region ../tasks/src/task-types/pr-review.ts
|
|
3468
|
-
var PrReviewSubject = Type$1.Object({
|
|
3469
|
-
title: Type$1.String({ minLength: 1 }),
|
|
3470
|
-
summary: Type$1.String({ minLength: 1 }),
|
|
3471
|
-
resourceUrls: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 }))),
|
|
3472
|
-
inspectionHints: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 })))
|
|
3473
|
-
}, {
|
|
3474
|
-
$id: "PrReviewSubject",
|
|
3475
|
-
additionalProperties: false
|
|
3476
|
-
});
|
|
3477
|
-
Type$1.Object({
|
|
3478
|
-
subject: PrReviewSubject,
|
|
3479
|
-
taskPrompt: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3480
|
-
successCriteria: SuccessCriteria,
|
|
3481
|
-
context: Type$1.Optional(TaskContext$1)
|
|
3482
|
-
}, {
|
|
3483
|
-
$id: "PrReviewInput",
|
|
3484
|
-
additionalProperties: false
|
|
3485
|
-
});
|
|
3486
|
-
var PrReviewScore = Type$1.Object({
|
|
3487
|
-
criterionId: Type$1.String({ minLength: 1 }),
|
|
3488
|
-
score: Type$1.Union([Type$1.Literal(0), Type$1.Literal(1)]),
|
|
3489
|
-
rationale: Type$1.String({ minLength: 1 })
|
|
3490
|
-
}, {
|
|
3491
|
-
$id: "PrReviewScore",
|
|
3492
|
-
additionalProperties: false
|
|
3493
|
-
});
|
|
3494
|
-
Type$1.Object({
|
|
3495
|
-
scores: Type$1.Array(PrReviewScore, { minItems: 1 }),
|
|
3496
|
-
composite: Type$1.Number({
|
|
3497
|
-
minimum: 0,
|
|
3498
|
-
maximum: 1
|
|
3499
|
-
}),
|
|
3500
|
-
verdict: Type$1.String({ minLength: 1 })
|
|
3501
|
-
}, {
|
|
3502
|
-
$id: "PrReviewOutput",
|
|
3503
|
-
additionalProperties: false
|
|
3504
|
-
});
|
|
3505
|
-
Type$1.Object({
|
|
3506
|
-
packId: Type$1.String({ format: "uuid" }),
|
|
3507
|
-
persist: Type$1.Optional(Type$1.Boolean()),
|
|
3508
|
-
pinned: Type$1.Optional(Type$1.Boolean()),
|
|
3509
|
-
successCriteria: Type$1.Optional(SuccessCriteria)
|
|
3510
|
-
}, {
|
|
3511
|
-
$id: "RenderPackInput",
|
|
3512
|
-
additionalProperties: false
|
|
3513
|
-
});
|
|
3514
|
-
Type$1.Object({
|
|
3515
|
-
renderedPackId: Type$1.Union([Type$1.String({ format: "uuid" }), Type$1.Null()]),
|
|
3516
|
-
renderedCid: Type$1.String({ minLength: 1 }),
|
|
3517
|
-
renderMethod: Type$1.String({ minLength: 1 }),
|
|
3518
|
-
byteSize: Type$1.Number({ minimum: 0 }),
|
|
3519
|
-
entriesRendered: Type$1.Number({ minimum: 0 }),
|
|
3520
|
-
summary: Type$1.String({ minLength: 1 }),
|
|
3521
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3522
|
-
}, {
|
|
3523
|
-
$id: "RenderPackOutput",
|
|
3524
|
-
additionalProperties: false
|
|
3525
|
-
});
|
|
3526
|
-
//#endregion
|
|
3527
|
-
//#region ../tasks/src/task-types/run-eval.ts
|
|
3528
|
-
/**
|
|
3529
|
-
* `run_eval` — execute a scenario prompt under a named variant for
|
|
3530
|
-
* later per-attempt grading by `judge_eval_attempt` tasks.
|
|
3531
|
-
*
|
|
3532
|
-
* output_kind: artifact
|
|
3533
|
-
* criteria: optional producer-only checks (when set,
|
|
3534
|
-
* output.verification is required — the judge rubric remains hidden
|
|
3535
|
-
* on downstream `judge_eval_attempt` tasks)
|
|
3536
|
-
* references: not required (scenario lives entirely in input)
|
|
3537
|
-
*/
|
|
3538
|
-
var RunEvalMode = Type$1.Union([Type$1.Literal("vitro"), Type$1.Literal("vivo")], { $id: "RunEvalMode" });
|
|
3539
|
-
var RunEvalWorkspace = Type$1.Union([
|
|
3540
|
-
Type$1.Literal("none"),
|
|
3541
|
-
Type$1.Literal("shared_mount"),
|
|
3542
|
-
Type$1.Literal("dedicated_worktree")
|
|
3543
|
-
], { $id: "RunEvalWorkspace" });
|
|
3544
|
-
var RunEvalExecution = Type$1.Object({
|
|
3545
|
-
mode: RunEvalMode,
|
|
3546
|
-
workspace: RunEvalWorkspace
|
|
3547
|
-
}, {
|
|
3548
|
-
$id: "RunEvalExecution",
|
|
3549
|
-
additionalProperties: false
|
|
3550
|
-
});
|
|
3551
|
-
/**
|
|
3552
|
-
* Producer-visible checks for `run_eval`. Deliberately forbids `rubric`
|
|
3553
|
-
* so the variant runner cannot see the downstream judge's answer key.
|
|
3554
|
-
* Keep the rest of the SuccessCriteria envelope available for generic
|
|
3555
|
-
* process / structure checks (`gates`, `assertions`, `sideEffects`).
|
|
3556
|
-
*/
|
|
3557
|
-
var RunEvalSuccessCriteria = Type$1.Object({
|
|
3558
|
-
version: Type$1.Literal(1),
|
|
3559
|
-
gates: Type$1.Optional(SuccessCriteria.properties.gates),
|
|
3560
|
-
assertions: Type$1.Optional(SuccessCriteria.properties.assertions),
|
|
3561
|
-
sideEffects: Type$1.Optional(SuccessCriteria.properties.sideEffects)
|
|
3562
|
-
}, {
|
|
3563
|
-
$id: "RunEvalSuccessCriteria",
|
|
3564
|
-
additionalProperties: false
|
|
3565
|
-
});
|
|
3566
|
-
Type$1.Object({
|
|
3567
|
-
scenario: Type$1.Object({
|
|
3568
|
-
prompt: Type$1.String({ minLength: 1 }),
|
|
3569
|
-
inputFiles: Type$1.Optional(Type$1.Array(Type$1.String({ minLength: 1 })))
|
|
3570
|
-
}, { additionalProperties: false }),
|
|
3571
|
-
variantLabel: Type$1.String({
|
|
3572
|
-
minLength: 1,
|
|
3573
|
-
maxLength: 64
|
|
3574
|
-
}),
|
|
3575
|
-
execution: RunEvalExecution,
|
|
3576
|
-
context: TaskContext$1,
|
|
3577
|
-
successCriteria: Type$1.Optional(RunEvalSuccessCriteria)
|
|
3578
|
-
}, {
|
|
3579
|
-
$id: "RunEvalInput",
|
|
3580
|
-
additionalProperties: false
|
|
3581
|
-
});
|
|
3582
|
-
var RunEvalArtifact = Type$1.Object({
|
|
3583
|
-
path: Type$1.String({ minLength: 1 }),
|
|
3584
|
-
cid: Type$1.String({ minLength: 1 })
|
|
3585
|
-
}, { additionalProperties: false });
|
|
3586
|
-
Type$1.Object({
|
|
3587
|
-
response: Type$1.String({ minLength: 1 }),
|
|
3588
|
-
artifacts: Type$1.Optional(Type$1.Array(RunEvalArtifact)),
|
|
3589
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3590
|
-
}, {
|
|
3591
|
-
$id: "RunEvalSubmission",
|
|
3592
|
-
additionalProperties: false
|
|
3593
|
-
});
|
|
3594
|
-
Type$1.Object({
|
|
3595
|
-
response: Type$1.String({ minLength: 1 }),
|
|
3596
|
-
artifacts: Type$1.Optional(Type$1.Array(RunEvalArtifact)),
|
|
3597
|
-
totalTokens: Type$1.Integer({ minimum: 0 }),
|
|
3598
|
-
durationMs: Type$1.Integer({ minimum: 0 }),
|
|
3599
|
-
traceparent: Type$1.Optional(Type$1.String({ minLength: 1 })),
|
|
3600
|
-
verification: Type$1.Optional(VerificationRecord)
|
|
3601
|
-
}, {
|
|
3602
|
-
$id: "RunEvalOutput",
|
|
3603
|
-
additionalProperties: false
|
|
3604
|
-
});
|
|
3605
|
-
//#endregion
|
|
3606
|
-
//#region ../tasks/src/task-type-registry.ts
|
|
3607
|
-
var schemaCids = null;
|
|
3608
|
-
function getTaskTypeRegistry() {
|
|
3609
|
-
if (!schemaCids) throw new Error("Task type registry not initialized. Call initTaskTypeRegistry() first.");
|
|
3610
|
-
return schemaCids;
|
|
3611
|
-
}
|
|
3612
|
-
new Proxy({}, { get(_, prop) {
|
|
3613
|
-
if (typeof prop !== "string") return void 0;
|
|
3614
|
-
return getTaskTypeRegistry().get(prop);
|
|
3615
|
-
} });
|
|
3616
|
-
//#endregion
|
|
3617
|
-
//#region ../tasks/src/wire.ts
|
|
3618
|
-
/**
|
|
3619
|
-
* Wire-format types for the MoltNet Task model.
|
|
3620
|
-
*
|
|
3621
|
-
* These schemas are the single source of truth for:
|
|
3622
|
-
* - `tasks`, `task_attempts`, `task_messages` DB columns (PR 1's Drizzle
|
|
3623
|
-
* schema must match these verbatim)
|
|
3624
|
-
* - REST request/response bodies (PR 4)
|
|
3625
|
-
* - `TaskReporter` output records (PR 0)
|
|
3626
|
-
*
|
|
3627
|
-
* Invariant: every property on `Task` is type-neutral (applies to all
|
|
3628
|
-
* `taskType`s). Type-specific payloads live inside `input` / `output`
|
|
3629
|
-
* JSONB, validated against schemas registered under `task_types`.
|
|
3630
|
-
*
|
|
3631
|
-
* Identity rule:
|
|
3632
|
-
* - claim/execute/sign → agent-only (`task_attempts.claimed_by_agent_id`)
|
|
3633
|
-
* - propose/cancel → agent XOR human (dual nullable FK + XOR check)
|
|
3634
|
-
*
|
|
3635
|
-
* See GH issue #852 for the full design snapshot.
|
|
3636
|
-
*/
|
|
3637
|
-
var TaskStatus = Type$1.Union([
|
|
3638
|
-
Type$1.Literal("waiting"),
|
|
3639
|
-
Type$1.Literal("queued"),
|
|
3640
|
-
Type$1.Literal("dispatched"),
|
|
3641
|
-
Type$1.Literal("running"),
|
|
3642
|
-
Type$1.Literal("completed"),
|
|
3643
|
-
Type$1.Literal("failed"),
|
|
3644
|
-
Type$1.Literal("cancelled"),
|
|
3645
|
-
Type$1.Literal("expired")
|
|
3646
|
-
], { $id: "TaskStatus" });
|
|
3647
|
-
var TaskAttemptStatus = Type$1.Union([
|
|
3648
|
-
Type$1.Literal("claimed"),
|
|
3649
|
-
Type$1.Literal("running"),
|
|
3650
|
-
Type$1.Literal("completed"),
|
|
3651
|
-
Type$1.Literal("failed"),
|
|
3652
|
-
Type$1.Literal("cancelled"),
|
|
3653
|
-
Type$1.Literal("aborted"),
|
|
3654
|
-
Type$1.Literal("timed_out")
|
|
3655
|
-
], { $id: "TaskAttemptStatus" });
|
|
3656
|
-
var ExecutorTrustLevel = Type$1.Union([
|
|
3657
|
-
Type$1.Literal("selfDeclared"),
|
|
3658
|
-
Type$1.Literal("agentSigned"),
|
|
3659
|
-
Type$1.Literal("releaseVerifiedTool"),
|
|
3660
|
-
Type$1.Literal("sandboxAttested")
|
|
3661
|
-
], { $id: "ExecutorTrustLevel" });
|
|
3662
|
-
var OutputKind = Type$1.Union([Type$1.Literal("artifact"), Type$1.Literal("judgment")], { $id: "OutputKind" });
|
|
3663
|
-
var TaskMessageKind = Type$1.Union([
|
|
3664
|
-
Type$1.Literal("text_delta"),
|
|
3665
|
-
Type$1.Literal("tool_call_start"),
|
|
3666
|
-
Type$1.Literal("tool_call_end"),
|
|
3667
|
-
Type$1.Literal("turn_end"),
|
|
3668
|
-
Type$1.Literal("error"),
|
|
3669
|
-
Type$1.Literal("info")
|
|
3670
|
-
], { $id: "TaskMessageKind" });
|
|
3671
|
-
var Uuid = Type$1.String({ format: "uuid" });
|
|
3672
|
-
var Cid = Type$1.String({ minLength: 1 });
|
|
3673
|
-
var IsoTimestamp = Type$1.String({ format: "date-time" });
|
|
3674
|
-
/**
|
|
3675
|
-
* Daemon-asserted runtime state stamped onto a `TaskAttemptSummary` at
|
|
3676
|
-
* attempt-completion time. The server persists this block verbatim and
|
|
3677
|
-
* exposes `slotResumableUntil` as a legacy/local warm-slot hint; task
|
|
3678
|
-
* continuation eligibility is based on the completed source attempt and
|
|
3679
|
-
* daemon-side claim-affinity/runtime-session recovery. The block carries
|
|
3680
|
-
* its own `reportedAt` so consumers can reason about staleness without
|
|
3681
|
-
* reading documentation. All daemon-asserted state lives here —
|
|
3682
|
-
* top-level attempt fields stay server-authoritative.
|
|
3683
|
-
*
|
|
3684
|
-
* Adding new fields requires explicit design review (intentional
|
|
3685
|
-
* boundary; see docs/superpowers/specs/2026-06-04-tasks-continue-design.md).
|
|
3686
|
-
*/
|
|
3687
|
-
var DaemonState = Type$1.Object({
|
|
3688
|
-
reportedAt: IsoTimestamp,
|
|
3689
|
-
slotResumableUntil: Type$1.Union([IsoTimestamp, Type$1.Null()])
|
|
3690
|
-
}, {
|
|
3691
|
-
$id: "DaemonState",
|
|
3692
|
-
additionalProperties: false
|
|
3693
|
-
});
|
|
3694
|
-
var ClaimConditionSchema = Type$1.Union([
|
|
3695
|
-
Type$1.Object({
|
|
3696
|
-
op: Type$1.Literal("all"),
|
|
3697
|
-
conditions: Type$1.Array(Type$1.Ref("ClaimCondition"), {
|
|
3698
|
-
minItems: 1,
|
|
3699
|
-
maxItems: 8
|
|
3700
|
-
})
|
|
3701
|
-
}, { additionalProperties: false }),
|
|
3702
|
-
Type$1.Object({
|
|
3703
|
-
op: Type$1.Literal("any"),
|
|
3704
|
-
conditions: Type$1.Array(Type$1.Ref("ClaimCondition"), {
|
|
3705
|
-
minItems: 1,
|
|
3706
|
-
maxItems: 8
|
|
3707
|
-
})
|
|
3708
|
-
}, { additionalProperties: false }),
|
|
3709
|
-
Type$1.Object({
|
|
3710
|
-
op: Type$1.Literal("task_status"),
|
|
3711
|
-
taskId: Uuid,
|
|
3712
|
-
statuses: Type$1.Array(Type$1.Ref("TaskStatus"), {
|
|
3713
|
-
minItems: 1,
|
|
3714
|
-
maxItems: 8
|
|
3715
|
-
})
|
|
3716
|
-
}, { additionalProperties: false }),
|
|
3717
|
-
Type$1.Object({
|
|
3718
|
-
op: Type$1.Literal("task_accepted"),
|
|
3719
|
-
taskId: Uuid
|
|
3720
|
-
}, { additionalProperties: false })
|
|
3721
|
-
], { $id: "ClaimCondition" });
|
|
3722
|
-
var ClaimConditionDefinition = Type$1.Unsafe(ClaimConditionSchema);
|
|
3723
|
-
Type$1.Unsafe(Type$1.Cyclic({ ClaimCondition: ClaimConditionDefinition }, "ClaimCondition", { $id: "ClaimCondition" }));
|
|
3724
|
-
/**
|
|
3725
|
-
* Reference to another task's output or an external artifact.
|
|
3726
|
-
* Embedded in `tasks.references` JSONB array.
|
|
3727
|
-
*/
|
|
3728
|
-
var TaskRef = Type$1.Object({
|
|
3729
|
-
taskId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3730
|
-
outputCid: Type$1.Optional(Cid),
|
|
3731
|
-
role: Type$1.Union([
|
|
3732
|
-
Type$1.Literal("judged_work"),
|
|
3733
|
-
Type$1.Literal("reviewed_diff"),
|
|
3734
|
-
Type$1.Literal("target_source"),
|
|
3735
|
-
Type$1.Literal("context")
|
|
3736
|
-
]),
|
|
3737
|
-
external: Type$1.Optional(Type$1.Object({
|
|
3738
|
-
kind: Type$1.Union([
|
|
3739
|
-
Type$1.Literal("github_pr"),
|
|
3740
|
-
Type$1.Literal("github_issue"),
|
|
3741
|
-
Type$1.Literal("http_url")
|
|
3742
|
-
]),
|
|
3743
|
-
pr: Type$1.Optional(Type$1.Number()),
|
|
3744
|
-
issue: Type$1.Optional(Type$1.Number()),
|
|
3745
|
-
url: Type$1.Optional(Type$1.String()),
|
|
3746
|
-
commit_sha: Type$1.Optional(Type$1.String()),
|
|
3747
|
-
snapshot_cid: Type$1.Optional(Cid)
|
|
3748
|
-
})),
|
|
3749
|
-
artifact: Type$1.Optional(Type$1.Object({
|
|
3750
|
-
cid: Cid,
|
|
3751
|
-
attemptN: Type$1.Optional(Type$1.Integer({ minimum: 1 })),
|
|
3752
|
-
kind: Type$1.Optional(Type$1.String({
|
|
3753
|
-
minLength: 1,
|
|
3754
|
-
maxLength: 100
|
|
3755
|
-
})),
|
|
3756
|
-
title: Type$1.Optional(Type$1.String({
|
|
3757
|
-
minLength: 1,
|
|
3758
|
-
maxLength: 255
|
|
3759
|
-
})),
|
|
3760
|
-
contentType: Type$1.Optional(Type$1.String({
|
|
3761
|
-
minLength: 1,
|
|
3762
|
-
maxLength: 200
|
|
3763
|
-
}))
|
|
3764
|
-
}, { additionalProperties: false }))
|
|
3765
|
-
}, {
|
|
3766
|
-
$id: "TaskRef",
|
|
3767
|
-
additionalProperties: false
|
|
3768
|
-
});
|
|
3769
|
-
/**
|
|
3770
|
-
* Token / cost accounting for one attempt.
|
|
3771
|
-
* Reported by the runtime; persisted per-attempt, also rolled up into
|
|
3772
|
-
* `TaskOutput.usage` for convenience.
|
|
3773
|
-
*/
|
|
3774
|
-
var TaskUsage = Type$1.Object({
|
|
3775
|
-
inputTokens: Type$1.Integer({ minimum: 0 }),
|
|
3776
|
-
outputTokens: Type$1.Integer({ minimum: 0 }),
|
|
3777
|
-
cacheReadTokens: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
3778
|
-
cacheWriteTokens: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
3779
|
-
toolCalls: Type$1.Optional(Type$1.Integer({ minimum: 0 })),
|
|
3780
|
-
model: Type$1.Optional(Type$1.String()),
|
|
3781
|
-
provider: Type$1.Optional(Type$1.String())
|
|
3782
|
-
}, {
|
|
3783
|
-
$id: "TaskUsage",
|
|
3784
|
-
additionalProperties: false
|
|
3785
|
-
});
|
|
3786
|
-
var TaskRetryDecision = Type$1.Union([Type$1.Literal("retry"), Type$1.Literal("do_not_retry")]);
|
|
3787
|
-
var TaskRetryConfidence = Type$1.Union([
|
|
3788
|
-
Type$1.Literal("low"),
|
|
3789
|
-
Type$1.Literal("medium"),
|
|
3790
|
-
Type$1.Literal("high")
|
|
3791
|
-
]);
|
|
3792
|
-
var TaskRetrySource = Type$1.Union([
|
|
3793
|
-
Type$1.Literal("explicit"),
|
|
3794
|
-
Type$1.Literal("deterministic"),
|
|
3795
|
-
Type$1.Literal("attempts_exhausted"),
|
|
3796
|
-
Type$1.Literal("triage"),
|
|
3797
|
-
Type$1.Literal("triage_failed")
|
|
3798
|
-
]);
|
|
3799
|
-
var TaskRetryInfo = Type$1.Object({
|
|
3800
|
-
source: TaskRetrySource,
|
|
3801
|
-
decision: Type$1.Optional(TaskRetryDecision),
|
|
3802
|
-
confidence: Type$1.Optional(TaskRetryConfidence),
|
|
3803
|
-
reason: Type$1.Optional(Type$1.String())
|
|
3804
|
-
}, {
|
|
3805
|
-
$id: "TaskRetryInfo",
|
|
3806
|
-
additionalProperties: false
|
|
3807
|
-
});
|
|
3808
|
-
/**
|
|
3809
|
-
* Structured error returned from a failed attempt.
|
|
3810
|
-
*/
|
|
3811
|
-
var TaskError = Type$1.Object({
|
|
3812
|
-
code: Type$1.String(),
|
|
3813
|
-
message: Type$1.String(),
|
|
3814
|
-
stack: Type$1.Optional(Type$1.String()),
|
|
3815
|
-
retryable: Type$1.Optional(Type$1.Boolean()),
|
|
3816
|
-
retry: Type$1.Optional(TaskRetryInfo)
|
|
3817
|
-
}, {
|
|
3818
|
-
$id: "TaskError",
|
|
3819
|
-
additionalProperties: false
|
|
3820
|
-
});
|
|
3821
|
-
Type$1.Object({
|
|
3822
|
-
agentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3823
|
-
humanId: Type$1.Union([Uuid, Type$1.Null()])
|
|
3824
|
-
}, {
|
|
3825
|
-
$id: "ActorPair",
|
|
3826
|
-
additionalProperties: false
|
|
3827
|
-
});
|
|
3828
|
-
Type$1.Object({
|
|
3829
|
-
id: Uuid,
|
|
3830
|
-
taskType: Type$1.String({ minLength: 1 }),
|
|
3831
|
-
title: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
3832
|
-
tags: Type$1.Array(Type$1.String()),
|
|
3833
|
-
teamId: Uuid,
|
|
3834
|
-
diaryId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3835
|
-
outputKind: OutputKind,
|
|
3836
|
-
input: Type$1.Record(Type$1.String(), Type$1.Unknown()),
|
|
3837
|
-
inputSchemaCid: Cid,
|
|
3838
|
-
inputCid: Cid,
|
|
3839
|
-
references: Type$1.Array(TaskRef),
|
|
3840
|
-
correlationId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3841
|
-
proposedByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3842
|
-
proposedByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3843
|
-
acceptedAttemptN: Type$1.Union([Type$1.Number(), Type$1.Null()]),
|
|
3844
|
-
claimCondition: Type$1.Union([Type$1.Unsafe(Type$1.Ref("ClaimCondition")), Type$1.Null()]),
|
|
3845
|
-
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
3846
|
-
allowedProfiles: Type$1.Array(RuntimeProfileRef, { maxItems: 16 }),
|
|
3847
|
-
status: TaskStatus,
|
|
3848
|
-
queuedAt: IsoTimestamp,
|
|
3849
|
-
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()], { description: "First time the task entered completed, failed, cancelled, or expired; null until terminal." }),
|
|
3850
|
-
expiresAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
3851
|
-
cancelledByAgentId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3852
|
-
cancelledByHumanId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3853
|
-
cancelReason: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
3854
|
-
maxAttempts: Type$1.Number({ minimum: 1 }),
|
|
3855
|
-
dispatchTimeoutSec: Type$1.Union([Type$1.Integer({
|
|
3856
|
-
minimum: 1,
|
|
3857
|
-
maximum: 86400
|
|
3858
|
-
}), Type$1.Null()]),
|
|
3859
|
-
runningTimeoutSec: Type$1.Union([Type$1.Integer({
|
|
3860
|
-
minimum: 1,
|
|
3861
|
-
maximum: 86400
|
|
3862
|
-
}), Type$1.Null()])
|
|
3863
|
-
}, {
|
|
3864
|
-
$id: "Task",
|
|
3865
|
-
additionalProperties: false
|
|
3866
|
-
});
|
|
3867
|
-
Type$1.Object({
|
|
3868
|
-
taskId: Uuid,
|
|
3869
|
-
attemptN: Type$1.Number({ minimum: 1 }),
|
|
3870
|
-
claimedByAgentId: Uuid,
|
|
3871
|
-
leaseId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3872
|
-
runtimeProfileId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3873
|
-
runtimeProfileRevision: Type$1.Union([Type$1.Integer({ minimum: 1 }), Type$1.Null()]),
|
|
3874
|
-
policySnapshotHash: Type$1.Union([Type$1.String({ pattern: "^sha256:[0-9a-f]{64}$" }), Type$1.Null()]),
|
|
3875
|
-
runtimeId: Type$1.Union([Uuid, Type$1.Null()]),
|
|
3876
|
-
claimedAt: IsoTimestamp,
|
|
3877
|
-
startedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
3878
|
-
completedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
3879
|
-
status: TaskAttemptStatus,
|
|
3880
|
-
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
3881
|
-
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
3882
|
-
claimedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
3883
|
-
claimedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
3884
|
-
completedExecutorFingerprint: Type$1.Union([Cid, Type$1.Null()]),
|
|
3885
|
-
completedExecutorManifest: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
3886
|
-
error: Type$1.Union([TaskError, Type$1.Null()]),
|
|
3887
|
-
usage: Type$1.Union([TaskUsage, Type$1.Null()]),
|
|
3888
|
-
contentSignature: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
3889
|
-
signedAt: Type$1.Union([IsoTimestamp, Type$1.Null()]),
|
|
3890
|
-
daemonState: Type$1.Union([DaemonState, Type$1.Null()])
|
|
3891
|
-
}, {
|
|
3892
|
-
$id: "TaskAttempt",
|
|
3893
|
-
additionalProperties: true
|
|
3894
|
-
});
|
|
3895
|
-
Type$1.Object({
|
|
3896
|
-
taskId: Uuid,
|
|
3897
|
-
attemptN: Type$1.Number({ minimum: 1 }),
|
|
3898
|
-
seq: Type$1.Number({
|
|
3899
|
-
minimum: 0,
|
|
3900
|
-
description: "Monotonically increasing integer assigned by the server. Use as the afterSeq cursor on the list-messages endpoint to poll for new messages without re-fetching earlier ones."
|
|
3901
|
-
}),
|
|
3902
|
-
timestamp: IsoTimestamp,
|
|
3903
|
-
kind: TaskMessageKind,
|
|
3904
|
-
payload: Type$1.Record(Type$1.String(), Type$1.Unknown())
|
|
3905
|
-
}, {
|
|
3906
|
-
$id: "TaskMessage",
|
|
3907
|
-
additionalProperties: false
|
|
3908
|
-
});
|
|
3909
|
-
Type$1.Object({
|
|
3910
|
-
taskId: Uuid,
|
|
3911
|
-
attemptN: Type$1.Number({ minimum: 1 }),
|
|
3912
|
-
status: Type$1.Union([
|
|
3913
|
-
Type$1.Literal("completed"),
|
|
3914
|
-
Type$1.Literal("failed"),
|
|
3915
|
-
Type$1.Literal("cancelled")
|
|
3916
|
-
]),
|
|
3917
|
-
output: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Unknown()), Type$1.Null()]),
|
|
3918
|
-
outputCid: Type$1.Union([Cid, Type$1.Null()]),
|
|
3919
|
-
usage: TaskUsage,
|
|
3920
|
-
durationMs: Type$1.Number({ minimum: 0 }),
|
|
3921
|
-
error: Type$1.Optional(TaskError),
|
|
3922
|
-
contentSignature: Type$1.Optional(Type$1.String())
|
|
3923
|
-
}, {
|
|
3924
|
-
$id: "TaskOutput",
|
|
3925
|
-
additionalProperties: false
|
|
3926
|
-
});
|
|
3927
|
-
Type$1.Object({
|
|
3928
|
-
runtimeId: Uuid,
|
|
3929
|
-
agentId: Uuid,
|
|
3930
|
-
timestamp: IsoTimestamp,
|
|
3931
|
-
status: Type$1.Union([
|
|
3932
|
-
Type$1.Literal("idle"),
|
|
3933
|
-
Type$1.Literal("busy"),
|
|
3934
|
-
Type$1.Literal("draining")
|
|
3935
|
-
]),
|
|
3936
|
-
activeTaskIds: Type$1.Array(Uuid),
|
|
3937
|
-
supportedTaskTypes: Type$1.Array(Type$1.String())
|
|
3938
|
-
}, {
|
|
3939
|
-
$id: "RuntimeHeartbeat",
|
|
3940
|
-
additionalProperties: false
|
|
3941
|
-
});
|
|
3942
|
-
//#endregion
|
|
3943
|
-
//#region src/snapshot.ts
|
|
3944
|
-
/**
|
|
3945
|
-
* Snapshot builder with auto-build and caching.
|
|
3946
|
-
*
|
|
3947
|
-
* Builds a Gondolin VM snapshot in two layers:
|
|
3948
|
-
* 1. Base (always): Alpine essentials, git, gh CLI, MoltNet CLI, agent user
|
|
3949
|
-
* 2. User setup commands (optional): arbitrary shell commands on top of the base
|
|
3950
|
-
*
|
|
3951
|
-
* Consumers provide raw shell commands — no abstraction over package managers
|
|
3952
|
-
* or runtimes. The base provides curl, git, tar, jq; everything else is up to
|
|
3953
|
-
* the setup commands.
|
|
3954
|
-
*
|
|
3955
|
-
* Caches in a platform-appropriate directory:
|
|
3956
|
-
* - macOS: ~/Library/Caches/moltnet/gondolin/
|
|
3957
|
-
* - Linux: ~/.cache/moltnet/gondolin/
|
|
3958
|
-
*
|
|
3959
|
-
* The cache key is a hash of the full config. When any input changes,
|
|
3960
|
-
* a new snapshot is built automatically.
|
|
3961
|
-
*/
|
|
3962
|
-
/** Alpine packages whose commands are guaranteed in every snapshot. */
|
|
3963
|
-
var BASE_ALPINE_PACKAGE_EXECUTABLES = {
|
|
3964
|
-
curl: "curl",
|
|
3965
|
-
file: "file",
|
|
3966
|
-
git: "git",
|
|
3967
|
-
jq: "jq",
|
|
3968
|
-
ripgrep: "rg",
|
|
3969
|
-
tar: "tar",
|
|
3970
|
-
xz: "xz"
|
|
3971
|
-
};
|
|
3972
|
-
/** Alpine packages required by every snapshot. */
|
|
3973
|
-
var BASE_ALPINE_PACKAGES = ["ca-certificates", ...Object.keys(BASE_ALPINE_PACKAGE_EXECUTABLES)];
|
|
3974
|
-
/** Commands guaranteed by the base Gondolin snapshot. */
|
|
3975
|
-
var GONDOLIN_BASE_EXECUTABLES = Object.freeze([
|
|
3976
|
-
...Object.values(BASE_ALPINE_PACKAGE_EXECUTABLES),
|
|
3977
|
-
"gh",
|
|
3978
|
-
"moltnet"
|
|
3979
|
-
].sort());
|
|
3980
|
-
/** gh CLI version installed in every snapshot. */
|
|
3981
|
-
var GH_VERSION = "2.74.0";
|
|
3982
|
-
/** MoltNet CLI version — downloaded as a binary, no Node needed. */
|
|
3983
|
-
var MOLTNET_CLI_VERSION = "1.37.0";
|
|
3984
|
-
/**
|
|
3985
|
-
* Resolve guest architecture from host (Gondolin VMs match host arch).
|
|
3986
|
-
*
|
|
3987
|
-
* The two naming conventions are NOT interchangeable:
|
|
3988
|
-
* - `gh` — GitHub release-asset suffix (gh CLI ships `linux_amd64.tar.gz`,
|
|
3989
|
-
* `linux_arm64.tar.gz`).
|
|
3990
|
-
* - `npm` — npm optionalDependencies naming, which mirrors Node's
|
|
3991
|
-
* `process.arch` values (`x64`, `arm64`). The MoltNet CLI is
|
|
3992
|
-
* published as `@themoltnet/cli-linux-x64` and
|
|
3993
|
-
* `@themoltnet/cli-linux-arm64`, NOT `cli-linux-amd64`.
|
|
3994
|
-
*/
|
|
3995
|
-
function getGuestArch() {
|
|
3996
|
-
if (process.arch === "arm64") return {
|
|
3997
|
-
gh: "linux_arm64",
|
|
3998
|
-
npm: "linux-arm64"
|
|
3999
|
-
};
|
|
4000
|
-
return {
|
|
4001
|
-
gh: "linux_amd64",
|
|
4002
|
-
npm: "linux-x64"
|
|
4003
|
-
};
|
|
4004
|
-
}
|
|
4005
|
-
/** Hosts reachable during snapshot build. */
|
|
4006
|
-
var SETUP_ALLOWED_HOSTS = [
|
|
4007
|
-
"dl-cdn.alpinelinux.org",
|
|
4008
|
-
"*.alpinelinux.org",
|
|
4009
|
-
"registry.npmjs.org",
|
|
4010
|
-
"*.npmjs.org",
|
|
4011
|
-
"nodejs.org",
|
|
4012
|
-
"*.nodejs.org",
|
|
4013
|
-
"unofficial-builds.nodejs.org",
|
|
4014
|
-
"github.com",
|
|
4015
|
-
"*.github.com",
|
|
4016
|
-
"*.githubusercontent.com",
|
|
4017
|
-
"objects.githubusercontent.com"
|
|
4018
|
-
];
|
|
4019
|
-
var DEFAULT_CONFIG = {};
|
|
4020
|
-
function getCacheDir() {
|
|
4021
|
-
if (process.platform === "darwin") return path.join(process.env.HOME ?? "/tmp", "Library", "Caches", "moltnet", "gondolin");
|
|
4022
|
-
const base = process.env.XDG_CACHE_HOME ?? path.join(process.env.HOME ?? "/tmp", ".cache");
|
|
4023
|
-
return path.join(base, "moltnet", "gondolin");
|
|
4024
|
-
}
|
|
4025
|
-
function computeConfigHash(config) {
|
|
4026
|
-
const h = createHash("sha256");
|
|
4027
|
-
h.update(JSON.stringify({
|
|
4028
|
-
baseAlpine: BASE_ALPINE_PACKAGES,
|
|
4029
|
-
ghVersion: GH_VERSION,
|
|
4030
|
-
cliVersion: MOLTNET_CLI_VERSION,
|
|
4031
|
-
config
|
|
4032
|
-
}));
|
|
4033
|
-
return h.digest("hex").slice(0, 12);
|
|
4034
|
-
}
|
|
4035
|
-
function getSnapshotPath(config) {
|
|
4036
|
-
const hash = computeConfigHash(config);
|
|
4037
|
-
const dir = path.join(getCacheDir(), `v2-${hash}`);
|
|
4038
|
-
return path.join(dir, "snapshot.qcow2");
|
|
4039
|
-
}
|
|
4040
|
-
/**
|
|
4041
|
-
* Ensure a cached snapshot exists, building one if needed.
|
|
4042
|
-
* Returns the absolute path to the qcow2 checkpoint file.
|
|
4043
|
-
*/
|
|
4044
|
-
async function ensureSnapshot(options = {}) {
|
|
4045
|
-
const config = options.config ?? DEFAULT_CONFIG;
|
|
4046
|
-
const log = options.onProgress ?? (() => {});
|
|
4047
|
-
const maxCached = options.maxCached ?? 1;
|
|
4048
|
-
const snapshotPath = getSnapshotPath(config);
|
|
4049
|
-
const snapshotDir = path.dirname(snapshotPath);
|
|
4050
|
-
if (existsSync(snapshotPath)) {
|
|
4051
|
-
log(`snapshot cache hit: ${snapshotPath}`);
|
|
4052
|
-
return snapshotPath;
|
|
4053
|
-
}
|
|
4054
|
-
log("snapshot cache miss — building (this takes 1-3 minutes)...");
|
|
4055
|
-
mkdirSync(snapshotDir, { recursive: true });
|
|
4056
|
-
const overlayPath = path.join(snapshotDir, "build.overlay.qcow2");
|
|
4057
|
-
if (existsSync(overlayPath)) rmSync(overlayPath);
|
|
4058
|
-
if (existsSync(snapshotPath)) rmSync(snapshotPath);
|
|
4059
|
-
log("resolving alpine-base image...");
|
|
4060
|
-
const assets = loadGuestAssets((await ensureImageSelector("alpine-base")).assetDir);
|
|
4061
|
-
const overlaySize = config.overlaySize ?? "3G";
|
|
4062
|
-
log(`creating qcow2 overlay (${overlaySize})...`);
|
|
4063
|
-
execFileSync("qemu-img", [
|
|
4064
|
-
"create",
|
|
4065
|
-
"-f",
|
|
4066
|
-
"qcow2",
|
|
4067
|
-
"-F",
|
|
4068
|
-
"raw",
|
|
4069
|
-
"-b",
|
|
4070
|
-
assets.rootfsPath,
|
|
4071
|
-
overlayPath,
|
|
4072
|
-
overlaySize
|
|
4073
|
-
], { stdio: "pipe" });
|
|
4074
|
-
const { httpHooks } = createHttpHooks({ allowedHosts: [...SETUP_ALLOWED_HOSTS, ...config.allowedHosts ?? []] });
|
|
4075
|
-
log("booting VM for setup...");
|
|
4076
|
-
const vm = await VM.create({
|
|
4077
|
-
httpHooks,
|
|
4078
|
-
env: {
|
|
4079
|
-
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go/bin",
|
|
4080
|
-
HOME: "/root",
|
|
4081
|
-
GOROOT: "/usr/lib/go",
|
|
4082
|
-
GOPATH: "/root/go"
|
|
4083
|
-
},
|
|
4084
|
-
sandbox: {
|
|
4085
|
-
rootDiskPath: overlayPath,
|
|
4086
|
-
rootDiskFormat: "qcow2",
|
|
4087
|
-
rootDiskReadOnly: false,
|
|
4088
|
-
rootDiskDeleteOnClose: false
|
|
4089
|
-
}
|
|
4090
|
-
});
|
|
4091
|
-
try {
|
|
4092
|
-
await buildSnapshot(vm, config, log);
|
|
4093
|
-
log("creating checkpoint...");
|
|
4094
|
-
await vm.checkpoint(snapshotPath);
|
|
4095
|
-
log(`snapshot saved: ${snapshotPath}`);
|
|
4096
|
-
} finally {
|
|
4097
|
-
try {
|
|
4098
|
-
await vm.close();
|
|
4099
|
-
} catch {}
|
|
4100
|
-
if (existsSync(overlayPath)) rmSync(overlayPath, { force: true });
|
|
4101
|
-
}
|
|
4102
|
-
pruneOldSnapshots(maxCached, snapshotDir);
|
|
4103
|
-
return snapshotPath;
|
|
4104
|
-
}
|
|
4105
|
-
/** Helper: run a command in the VM, throw on failure. */
|
|
4106
|
-
async function run(vm, log, label, cmd) {
|
|
4107
|
-
log(label);
|
|
4108
|
-
const r = await vm.exec(cmd);
|
|
4109
|
-
if (r.exitCode !== 0) {
|
|
4110
|
-
const output = [r.stderr, r.stdout].filter(Boolean).join("\n").slice(0, 800);
|
|
4111
|
-
throw new Error(`snapshot build "${label}" failed (exit ${r.exitCode}):\n${output}`);
|
|
4112
|
-
}
|
|
4113
|
-
}
|
|
4114
|
-
async function buildSnapshot(vm, config, log) {
|
|
4115
|
-
await run(vm, log, "resizing rootfs...", "apk add --no-cache e2fsprogs-extra >/dev/null 2>&1 && resize2fs /dev/vda 2>/dev/null");
|
|
4116
|
-
await run(vm, log, `installing base packages: ${BASE_ALPINE_PACKAGES.join(" ")}`, `apk add --no-cache ${BASE_ALPINE_PACKAGES.join(" ")}`);
|
|
4117
|
-
const arch = getGuestArch();
|
|
4118
|
-
await run(vm, log, `installing gh ${GH_VERSION} (${arch.gh})...`, `sh -eu -c '
|
|
4119
|
-
curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_${arch.gh}.tar.gz" -o /tmp/gh.tar.gz
|
|
4120
|
-
tar -xzf /tmp/gh.tar.gz -C /tmp
|
|
4121
|
-
mv /tmp/gh_${GH_VERSION}_${arch.gh}/bin/gh /usr/local/bin/gh
|
|
4122
|
-
chmod +x /usr/local/bin/gh
|
|
4123
|
-
rm -rf /tmp/gh.tar.gz /tmp/gh_${GH_VERSION}_${arch.gh}
|
|
4124
|
-
gh --version
|
|
4125
|
-
'`);
|
|
4126
|
-
await run(vm, log, `installing moltnet CLI ${MOLTNET_CLI_VERSION} (${arch.npm})...`, `sh -eu -c '
|
|
4127
|
-
curl -fsSL "https://registry.npmjs.org/@themoltnet/cli-${arch.npm}/-/cli-${arch.npm}-${MOLTNET_CLI_VERSION}.tgz" -o /tmp/moltnet.tgz
|
|
4128
|
-
tar -xzf /tmp/moltnet.tgz -C /tmp
|
|
4129
|
-
mv /tmp/package/bin/moltnet /usr/local/bin/moltnet
|
|
4130
|
-
chmod +x /usr/local/bin/moltnet
|
|
4131
|
-
rm -rf /tmp/moltnet.tgz /tmp/package
|
|
4132
|
-
'`);
|
|
4133
|
-
await run(vm, log, "creating agent user...", `sh -eu -c '
|
|
4134
|
-
addgroup -g 501 agent 2>/dev/null || true
|
|
4135
|
-
adduser -D -u 501 -G agent -h /home/agent -s /bin/sh agent 2>/dev/null || true
|
|
4136
|
-
mkdir -p /home/agent/.moltnet /home/agent/.cache
|
|
4137
|
-
chown -R agent:agent /home/agent
|
|
4138
|
-
chmod 644 /etc/gondolin/mitm/ca.crt 2>/dev/null || true
|
|
4139
|
-
'`);
|
|
4140
|
-
await run(vm, log, "configuring DNS resolvers...", `sh -c 'echo "nameserver 8.8.8.8
|
|
4141
|
-
nameserver 1.1.1.1" > /etc/resolv.conf'`);
|
|
4142
|
-
if (config.setupCommands?.length) for (let i = 0; i < config.setupCommands.length; i++) await run(vm, log, `setup [${i + 1}/${config.setupCommands.length}]...`, config.setupCommands[i]);
|
|
4143
|
-
}
|
|
4144
|
-
function pruneOldSnapshots(maxCached, currentDir) {
|
|
4145
|
-
const cacheRoot = getCacheDir();
|
|
4146
|
-
if (!existsSync(cacheRoot)) return;
|
|
4147
|
-
const entries = readdirSync(cacheRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("v")).map((e) => {
|
|
4148
|
-
const fullPath = path.join(cacheRoot, e.name);
|
|
4149
|
-
return {
|
|
4150
|
-
path: fullPath,
|
|
4151
|
-
mtime: statSync(fullPath).mtimeMs
|
|
4152
|
-
};
|
|
4153
|
-
}).sort((a, b) => b.mtime - a.mtime);
|
|
4154
|
-
for (const entry of entries.slice(maxCached + 1)) if (entry.path !== currentDir) rmSync(entry.path, {
|
|
4155
|
-
recursive: true,
|
|
4156
|
-
force: true
|
|
2843
|
+
function definePiBrokeredHttpSecret(options) {
|
|
2844
|
+
const descriptor = canonicalizeBrokeredHttpSecretDescriptor(options);
|
|
2845
|
+
return Object.freeze({
|
|
2846
|
+
kind: "brokered_http_secret",
|
|
2847
|
+
descriptor,
|
|
2848
|
+
resolve: options.resolve
|
|
4157
2849
|
});
|
|
4158
2850
|
}
|
|
4159
|
-
//#endregion
|
|
4160
|
-
//#region src/runtime-definition.ts
|
|
4161
|
-
var PI_RUNTIME_DEFINITION_VERSION = "moltnet:pi-runtime:v1";
|
|
4162
|
-
var PI_EXECUTOR_MANIFEST_VERSION = "moltnet:executor-manifest:v1";
|
|
4163
2851
|
function definePiTool(input, options = {}) {
|
|
4164
2852
|
if ("descriptor" in input) {
|
|
4165
2853
|
assertToolName(input.descriptor.name);
|
|
@@ -4216,7 +2904,7 @@ function defineGondolinTemplate(options) {
|
|
|
4216
2904
|
executables,
|
|
4217
2905
|
resumeCommands,
|
|
4218
2906
|
async resolve(context = {}) {
|
|
4219
|
-
const checkpointPath = options.resolveCheckpoint ? await options.resolveCheckpoint(context) : options.checkpointPath ?? await ensureSnapshot({
|
|
2907
|
+
const checkpointPath = options.resolveCheckpoint ? await options.resolveCheckpoint(context) : options.checkpointPath ?? await ensureSnapshot$1({
|
|
4220
2908
|
config: options.snapshot,
|
|
4221
2909
|
onProgress: context.onProgress
|
|
4222
2910
|
});
|
|
@@ -4249,17 +2937,28 @@ function definePiRuntime(options) {
|
|
|
4249
2937
|
const names = /* @__PURE__ */ new Map();
|
|
4250
2938
|
for (const tool of options.tools ?? []) claimToolName(names, tool.descriptor.name, "tool contribution");
|
|
4251
2939
|
for (const extension of options.extensions ?? []) for (const name of extension.declaredTools) claimToolName(names, name, `extension "${extension.id}"`);
|
|
2940
|
+
const secretIds = /* @__PURE__ */ new Set();
|
|
2941
|
+
const secretEnvNames = /* @__PURE__ */ new Set();
|
|
2942
|
+
for (const secret of options.brokeredHttpSecrets ?? []) {
|
|
2943
|
+
const { id, guestEnv } = secret.descriptor;
|
|
2944
|
+
if (secretIds.has(id)) throw new Error(`Duplicate brokered HTTP secret id "${id}"`);
|
|
2945
|
+
if (secretEnvNames.has(guestEnv)) throw new Error(`Duplicate brokered HTTP secret guest env "${guestEnv}"`);
|
|
2946
|
+
secretIds.add(id);
|
|
2947
|
+
secretEnvNames.add(guestEnv);
|
|
2948
|
+
}
|
|
4252
2949
|
return Object.freeze({
|
|
4253
2950
|
schemaVersion: PI_RUNTIME_DEFINITION_VERSION,
|
|
4254
2951
|
id: options.id,
|
|
4255
2952
|
version: options.version,
|
|
4256
2953
|
runtimeKind: options.runtimeKind ?? "gondolin_pi",
|
|
4257
2954
|
vm: options.vm,
|
|
2955
|
+
brokeredHttpSecrets: Object.freeze([...options.brokeredHttpSecrets ?? []]),
|
|
4258
2956
|
tools: Object.freeze([...options.tools ?? []]),
|
|
4259
2957
|
extensions: Object.freeze([...options.extensions ?? []])
|
|
4260
2958
|
});
|
|
4261
2959
|
}
|
|
4262
2960
|
async function buildPiExecutorManifest(input) {
|
|
2961
|
+
const brokeredHttpSecrets = input.runtime.brokeredHttpSecrets ?? [];
|
|
4263
2962
|
const descriptors = [...(input.builtInTools ?? []).map((descriptor) => ({
|
|
4264
2963
|
descriptor,
|
|
4265
2964
|
scope: "parent_and_subagents"
|
|
@@ -4299,6 +2998,14 @@ async function buildPiExecutorManifest(input) {
|
|
|
4299
2998
|
templateFingerprint: input.template.fingerprint,
|
|
4300
2999
|
guestAssetBuildId: input.template.guestAssetBuildId
|
|
4301
3000
|
},
|
|
3001
|
+
...brokeredHttpSecrets.length > 0 && { brokeredHttpSecrets: brokeredHttpSecrets.map(({ descriptor }) => ({
|
|
3002
|
+
id: descriptor.id,
|
|
3003
|
+
guestEnv: descriptor.guestEnv,
|
|
3004
|
+
hosts: [...descriptor.hosts],
|
|
3005
|
+
protocol: descriptor.protocol ?? "https",
|
|
3006
|
+
ports: [...descriptor.ports ?? [descriptor.protocol === "http" ? 80 : 443]],
|
|
3007
|
+
required: descriptor.required !== false
|
|
3008
|
+
})).sort((left, right) => left.id.localeCompare(right.id)) },
|
|
4302
3009
|
tools,
|
|
4303
3010
|
extensions: input.runtime.extensions.map((extension) => ({
|
|
4304
3011
|
id: extension.id,
|
|
@@ -4308,6 +3015,109 @@ async function buildPiExecutorManifest(input) {
|
|
|
4308
3015
|
executables: input.template.executables
|
|
4309
3016
|
};
|
|
4310
3017
|
}
|
|
3018
|
+
async function materializePiBrokeredHttpSecrets(input) {
|
|
3019
|
+
const timeoutMs = input.timeoutMs ?? 3e4;
|
|
3020
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS) throw new Error(`Brokered HTTP secret resolution timeout must be an integer between 1 and ${MAX_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS}`);
|
|
3021
|
+
if (input.signal?.aborted) throw brokeredHttpSecretAbortError();
|
|
3022
|
+
const contributions = input.runtime.brokeredHttpSecrets ?? [];
|
|
3023
|
+
const batchController = new AbortController();
|
|
3024
|
+
const abortBatchFromAttempt = () => batchController.abort(input.signal?.reason);
|
|
3025
|
+
input.signal?.addEventListener("abort", abortBatchFromAttempt, { once: true });
|
|
3026
|
+
try {
|
|
3027
|
+
const outcomes = await Promise.all(contributions.map((contribution, index) => materializeSinglePiBrokeredHttpSecret({
|
|
3028
|
+
contribution,
|
|
3029
|
+
context: input.context,
|
|
3030
|
+
batchSignal: batchController.signal,
|
|
3031
|
+
timeoutMs,
|
|
3032
|
+
index
|
|
3033
|
+
}).then((outcome) => {
|
|
3034
|
+
if (outcome.kind === "failure" && !batchController.signal.aborted) batchController.abort(outcome.error);
|
|
3035
|
+
return outcome;
|
|
3036
|
+
})));
|
|
3037
|
+
if (input.signal?.aborted) throw brokeredHttpSecretAbortError();
|
|
3038
|
+
const failures = outcomes.filter((outcome) => outcome.kind === "failure").sort((left, right) => Number(left.error.retryable) - Number(right.error.retryable) || left.index - right.index);
|
|
3039
|
+
if (failures[0]) throw failures[0].error;
|
|
3040
|
+
return outcomes.filter((outcome) => outcome.kind === "success").sort((left, right) => left.index - right.index).map(({ binding }) => binding);
|
|
3041
|
+
} finally {
|
|
3042
|
+
input.signal?.removeEventListener("abort", abortBatchFromAttempt);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
/** One auditable resolver lifecycle; the batch coordinator owns precedence. */
|
|
3046
|
+
async function materializeSinglePiBrokeredHttpSecret(input) {
|
|
3047
|
+
const { descriptor, resolve } = input.contribution;
|
|
3048
|
+
if (input.batchSignal.aborted) return {
|
|
3049
|
+
kind: "batch-abort",
|
|
3050
|
+
index: input.index
|
|
3051
|
+
};
|
|
3052
|
+
const controller = new AbortController();
|
|
3053
|
+
let timedOut = false;
|
|
3054
|
+
const abortFromBatch = () => controller.abort(input.batchSignal.reason);
|
|
3055
|
+
input.batchSignal.addEventListener("abort", abortFromBatch, { once: true });
|
|
3056
|
+
const timeout = setTimeout(() => {
|
|
3057
|
+
timedOut = true;
|
|
3058
|
+
controller.abort();
|
|
3059
|
+
}, input.timeoutMs);
|
|
3060
|
+
const abortOutcome = new Promise((resolveAbort) => {
|
|
3061
|
+
const resolveFromAbort = () => resolveAbort({ kind: "abort" });
|
|
3062
|
+
if (controller.signal.aborted) resolveFromAbort();
|
|
3063
|
+
else controller.signal.addEventListener("abort", resolveFromAbort, { once: true });
|
|
3064
|
+
});
|
|
3065
|
+
try {
|
|
3066
|
+
let resolverOutcome;
|
|
3067
|
+
try {
|
|
3068
|
+
resolverOutcome = Promise.resolve(resolve({
|
|
3069
|
+
...input.context,
|
|
3070
|
+
signal: controller.signal
|
|
3071
|
+
})).then((value) => ({
|
|
3072
|
+
kind: "value",
|
|
3073
|
+
value
|
|
3074
|
+
}), (error) => ({
|
|
3075
|
+
kind: "error",
|
|
3076
|
+
error
|
|
3077
|
+
}));
|
|
3078
|
+
} catch (error) {
|
|
3079
|
+
resolverOutcome = Promise.resolve({
|
|
3080
|
+
kind: "error",
|
|
3081
|
+
error
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
const outcome = await Promise.race([resolverOutcome, abortOutcome]);
|
|
3085
|
+
if (outcome.kind === "abort") {
|
|
3086
|
+
if (!timedOut) return {
|
|
3087
|
+
kind: "batch-abort",
|
|
3088
|
+
index: input.index
|
|
3089
|
+
};
|
|
3090
|
+
return {
|
|
3091
|
+
kind: "failure",
|
|
3092
|
+
index: input.index,
|
|
3093
|
+
error: new PiBrokeredHttpSecretResolutionError(descriptor.id, `Brokered HTTP secret "${descriptor.id}" resolution timed out`, true)
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
if (outcome.kind === "error") return {
|
|
3097
|
+
kind: "failure",
|
|
3098
|
+
index: input.index,
|
|
3099
|
+
error: outcome.error instanceof PiBrokeredHttpSecretResolutionError ? outcome.error : new PiBrokeredHttpSecretResolutionError(descriptor.id, `Brokered HTTP secret "${descriptor.id}" resolution failed`, true)
|
|
3100
|
+
};
|
|
3101
|
+
if ((outcome.value === void 0 || outcome.value === "") && descriptor.required !== false) return {
|
|
3102
|
+
kind: "failure",
|
|
3103
|
+
index: input.index,
|
|
3104
|
+
error: new PiBrokeredHttpSecretResolutionError(descriptor.id, `Required brokered HTTP secret "${descriptor.id}" is unavailable`, false)
|
|
3105
|
+
};
|
|
3106
|
+
return {
|
|
3107
|
+
kind: "success",
|
|
3108
|
+
index: input.index,
|
|
3109
|
+
binding: {
|
|
3110
|
+
...descriptor,
|
|
3111
|
+
hosts: [...descriptor.hosts],
|
|
3112
|
+
ports: descriptor.ports ? [...descriptor.ports] : void 0,
|
|
3113
|
+
value: outcome.value
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
} finally {
|
|
3117
|
+
clearTimeout(timeout);
|
|
3118
|
+
input.batchSignal.removeEventListener("abort", abortFromBatch);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
4311
3121
|
async function materializePiTools(input) {
|
|
4312
3122
|
const contributions = input.runtime.tools.filter((tool) => input.target === "parent" || tool.scope === "parent_and_subagents");
|
|
4313
3123
|
return (await Promise.all(contributions.map(async (contribution) => {
|
|
@@ -4372,633 +3182,6 @@ function assertRuntimeKind(value) {
|
|
|
4372
3182
|
if (!RUNTIME_PROFILE_RUNTIME_KIND_REGEXP.test(value)) throw new Error(`Invalid runtime kind "${value}"`);
|
|
4373
3183
|
}
|
|
4374
3184
|
//#endregion
|
|
4375
|
-
//#region src/abort-utils.ts
|
|
4376
|
-
function throwIfAborted(signal, label) {
|
|
4377
|
-
if (!signal?.aborted) return;
|
|
4378
|
-
throw abortError(label, signal);
|
|
4379
|
-
}
|
|
4380
|
-
function abortError(label, signal) {
|
|
4381
|
-
const reason = signal.reason;
|
|
4382
|
-
const suffix = reason instanceof Error ? reason.message : reason === void 0 ? "aborted" : String(reason);
|
|
4383
|
-
const err = /* @__PURE__ */ new Error(`${label} aborted: ${suffix}`);
|
|
4384
|
-
err.name = "AbortError";
|
|
4385
|
-
return err;
|
|
4386
|
-
}
|
|
4387
|
-
function cleanupLateResource(resourcePromise, opts) {
|
|
4388
|
-
resourcePromise.then(async (resource) => {
|
|
4389
|
-
try {
|
|
4390
|
-
await opts.cleanup(resource);
|
|
4391
|
-
} catch (err) {
|
|
4392
|
-
opts.onCleanupError?.(err);
|
|
4393
|
-
}
|
|
4394
|
-
}, () => {});
|
|
4395
|
-
}
|
|
4396
|
-
async function abortableResource(opts) {
|
|
4397
|
-
const { signal } = opts;
|
|
4398
|
-
if (!signal) return opts.promise;
|
|
4399
|
-
throwIfAborted(signal, opts.label);
|
|
4400
|
-
const resourcePromise = Promise.resolve(opts.promise);
|
|
4401
|
-
const abortPromise = new Promise((_, reject) => {
|
|
4402
|
-
const abort = () => {
|
|
4403
|
-
cleanupLateResource(resourcePromise, opts);
|
|
4404
|
-
reject(abortError(opts.label, signal));
|
|
4405
|
-
};
|
|
4406
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
4407
|
-
resourcePromise.then(() => signal.removeEventListener("abort", abort), () => signal.removeEventListener("abort", abort));
|
|
4408
|
-
});
|
|
4409
|
-
return Promise.race([resourcePromise, abortPromise]);
|
|
4410
|
-
}
|
|
4411
|
-
async function delay(ms, signal, label) {
|
|
4412
|
-
if (!signal) {
|
|
4413
|
-
await new Promise((resolve) => {
|
|
4414
|
-
setTimeout(resolve, ms);
|
|
4415
|
-
});
|
|
4416
|
-
return;
|
|
4417
|
-
}
|
|
4418
|
-
throwIfAborted(signal, label);
|
|
4419
|
-
await new Promise((resolve, reject) => {
|
|
4420
|
-
const listener = () => {
|
|
4421
|
-
clearTimeout(timeout);
|
|
4422
|
-
reject(abortError(label, signal));
|
|
4423
|
-
};
|
|
4424
|
-
const timeout = setTimeout(() => {
|
|
4425
|
-
signal.removeEventListener("abort", listener);
|
|
4426
|
-
resolve();
|
|
4427
|
-
}, ms);
|
|
4428
|
-
signal.addEventListener("abort", listener, { once: true });
|
|
4429
|
-
});
|
|
4430
|
-
}
|
|
4431
|
-
//#endregion
|
|
4432
|
-
//#region src/vm-manager.ts
|
|
4433
|
-
/**
|
|
4434
|
-
* Memory-backed VFS mount used by the daemon to inject task context
|
|
4435
|
-
* (#943 slice 1.5). This is a separate top-level mount because Gondolin
|
|
4436
|
-
* mounts can't nest. The agent's Gondolin-bound Read tool accepts paths
|
|
4437
|
-
* under this prefix (see toGuestPath in tool-operations.ts).
|
|
4438
|
-
*
|
|
4439
|
-
* Why MemoryProvider rather than a path under the workspace mount:
|
|
4440
|
-
* - Injected task context is ephemeral by intent: per-task-attempt input
|
|
4441
|
-
* scoped to the VM lifetime. MemoryProvider models that exactly —
|
|
4442
|
-
* in-memory, per-VM-instance, zero host artefacts, automatic
|
|
4443
|
-
* cleanup on VM close.
|
|
4444
|
-
* - Writing under the workspace mount fails in worktrees because we symlink
|
|
4445
|
-
* `.moltnet/` to the main repo (so credentials are reachable from
|
|
4446
|
-
* worktrees), and Gondolin's RealFSProvider correctly refuses to
|
|
4447
|
-
* create paths whose ancestors' realpath escapes the mount root.
|
|
4448
|
-
* That refusal is a deliberate sandbox-escape protection, not a
|
|
4449
|
-
* bug. See diary semantic entry cd27d9d3-efdc-4aec-ac0d-5fd8ce258d1f
|
|
4450
|
-
* and episodic 7affbfeb-18a2-4963-aeac-c177eb2afa2d for the full
|
|
4451
|
-
* investigation and the alternatives we rejected.
|
|
4452
|
-
*/
|
|
4453
|
-
var GUEST_TASK_CONTEXT_MOUNT = "/moltnet-task-context";
|
|
4454
|
-
function resolveVfsShadowConfig(config) {
|
|
4455
|
-
const patterns = config?.vfs?.shadow ?? [];
|
|
4456
|
-
if (patterns.length === 0) return {
|
|
4457
|
-
mode: "none",
|
|
4458
|
-
patterns: []
|
|
4459
|
-
};
|
|
4460
|
-
return {
|
|
4461
|
-
mode: config?.vfs?.shadowMode ?? "tmpfs",
|
|
4462
|
-
patterns
|
|
4463
|
-
};
|
|
4464
|
-
}
|
|
4465
|
-
function shouldRunResumeCommand(entry, ctx) {
|
|
4466
|
-
if (typeof entry === "string") return true;
|
|
4467
|
-
const workspaceModes = entry.when?.workspaceMode;
|
|
4468
|
-
if (workspaceModes && !workspaceModes.includes(ctx.workspaceMode)) return false;
|
|
4469
|
-
return true;
|
|
4470
|
-
}
|
|
4471
|
-
function shouldShadowNodeModulesPath(pathname) {
|
|
4472
|
-
const normalized = path.posix.normalize(pathname);
|
|
4473
|
-
return normalized === "/node_modules" || normalized.startsWith("/node_modules/") || normalized.endsWith("/node_modules") || normalized.includes("/node_modules/");
|
|
4474
|
-
}
|
|
4475
|
-
function isNodeModulesBinPath(pathname) {
|
|
4476
|
-
const normalized = path.posix.normalize(pathname);
|
|
4477
|
-
return normalized.includes("/node_modules/.bin/") || normalized.startsWith("/node_modules/.bin/");
|
|
4478
|
-
}
|
|
4479
|
-
var AutoParentMemoryProvider = class extends MemoryProvider {
|
|
4480
|
-
ensureParentDir(pathname) {
|
|
4481
|
-
const parent = path.posix.dirname(path.posix.normalize(pathname));
|
|
4482
|
-
if (!parent || parent === "/" || parent === ".") return;
|
|
4483
|
-
this.mkdirSync(parent, { recursive: true });
|
|
4484
|
-
}
|
|
4485
|
-
async mkdir(pathname, options) {
|
|
4486
|
-
this.ensureParentDir(pathname);
|
|
4487
|
-
return super.mkdir(pathname, options);
|
|
4488
|
-
}
|
|
4489
|
-
mkdirSync(pathname, options) {
|
|
4490
|
-
this.ensureParentDir(pathname);
|
|
4491
|
-
return super.mkdirSync(pathname, options);
|
|
4492
|
-
}
|
|
4493
|
-
async open(pathname, flags, mode) {
|
|
4494
|
-
if (isWriteFlag(flags)) this.ensureParentDir(pathname);
|
|
4495
|
-
return super.open(pathname, flags, isWriteFlag(flags) && isNodeModulesBinPath(pathname) ? (mode ?? 493) | 73 : mode);
|
|
4496
|
-
}
|
|
4497
|
-
openSync(pathname, flags, mode) {
|
|
4498
|
-
if (isWriteFlag(flags)) this.ensureParentDir(pathname);
|
|
4499
|
-
return super.openSync(pathname, flags, isWriteFlag(flags) && isNodeModulesBinPath(pathname) ? (mode ?? 493) | 73 : mode);
|
|
4500
|
-
}
|
|
4501
|
-
};
|
|
4502
|
-
/**
|
|
4503
|
-
* Resolve the main worktree root (where .moltnet/ lives — it's untracked,
|
|
4504
|
-
* only exists in the main worktree, not in git worktrees).
|
|
4505
|
-
*/
|
|
4506
|
-
function findMainWorktree(startPath = process.cwd()) {
|
|
4507
|
-
let output;
|
|
4508
|
-
try {
|
|
4509
|
-
output = execFileSync("git", [
|
|
4510
|
-
"-C",
|
|
4511
|
-
startPath,
|
|
4512
|
-
"worktree",
|
|
4513
|
-
"list",
|
|
4514
|
-
"--porcelain"
|
|
4515
|
-
], {
|
|
4516
|
-
encoding: "utf8",
|
|
4517
|
-
stdio: "pipe"
|
|
4518
|
-
});
|
|
4519
|
-
} catch (err) {
|
|
4520
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4521
|
-
throw new Error(`Git worktree discovery requires a git repository: ${message}`);
|
|
4522
|
-
}
|
|
4523
|
-
for (const block of output.split("\n\n")) {
|
|
4524
|
-
const lines = block.split("\n");
|
|
4525
|
-
const wt = lines.find((l) => l.startsWith("worktree "));
|
|
4526
|
-
if (wt && !lines.some((l) => l === "bare")) return wt.replace("worktree ", "");
|
|
4527
|
-
}
|
|
4528
|
-
throw new Error("Could not find main git worktree");
|
|
4529
|
-
}
|
|
4530
|
-
function resolveVmAgentDir(config) {
|
|
4531
|
-
const rootDir = config.agentRootDir ?? findMainWorktree();
|
|
4532
|
-
return path.join(rootDir, ".moltnet", config.agentName);
|
|
4533
|
-
}
|
|
4534
|
-
function loadCredentials(agentDir, mode = "guest-config", onDiagnostic) {
|
|
4535
|
-
const moltnetPath = path.join(agentDir, "moltnet.json");
|
|
4536
|
-
const agentEnvPath = path.join(agentDir, "env");
|
|
4537
|
-
const piAgentDir = resolvePiCodingAgentDir();
|
|
4538
|
-
const piAuthPath = path.join(piAgentDir, "auth.json");
|
|
4539
|
-
const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
|
|
4540
|
-
if (mode === "host-authenticated") return {
|
|
4541
|
-
moltnetJson: "",
|
|
4542
|
-
agentEnvRaw: "",
|
|
4543
|
-
piAuthJson,
|
|
4544
|
-
agentEnv: {},
|
|
4545
|
-
gitconfig: null,
|
|
4546
|
-
sshPrivateKey: null,
|
|
4547
|
-
sshPublicKey: null,
|
|
4548
|
-
allowedSigners: null,
|
|
4549
|
-
githubAppPem: null,
|
|
4550
|
-
githubAppPemFilename: null
|
|
4551
|
-
};
|
|
4552
|
-
const hasMoltnetJson = existsSync(moltnetPath);
|
|
4553
|
-
const hasAgentEnv = existsSync(agentEnvPath);
|
|
4554
|
-
if (!hasMoltnetJson || !hasAgentEnv) throw new Error(`Guest credential mode requires both ${moltnetPath} and ${agentEnvPath}`);
|
|
4555
|
-
const moltnetJson = readFileSync(moltnetPath, "utf8");
|
|
4556
|
-
const agentEnvRaw = readFileSync(agentEnvPath, "utf8");
|
|
4557
|
-
if (moltnetJson.trim() === "") throw new Error(`Agent configuration is empty: ${moltnetPath}`);
|
|
4558
|
-
const gitconfigPath = path.join(agentDir, "gitconfig");
|
|
4559
|
-
const gitconfig = existsSync(gitconfigPath) ? readFileSync(gitconfigPath, "utf8") : null;
|
|
4560
|
-
const sshDir = path.join(agentDir, "ssh");
|
|
4561
|
-
const sshPrivateKey = existsSync(path.join(sshDir, "id_ed25519")) ? readFileSync(path.join(sshDir, "id_ed25519"), "utf8") : null;
|
|
4562
|
-
const sshPublicKey = existsSync(path.join(sshDir, "id_ed25519.pub")) ? readFileSync(path.join(sshDir, "id_ed25519.pub"), "utf8") : null;
|
|
4563
|
-
const allowedSigners = existsSync(path.join(sshDir, "allowed_signers")) ? readFileSync(path.join(sshDir, "allowed_signers"), "utf8") : null;
|
|
4564
|
-
let githubAppPem = null;
|
|
4565
|
-
let githubAppPemFilename = null;
|
|
4566
|
-
const pemPath = (moltnetJson ? JSON.parse(moltnetJson) : null)?.github?.private_key_path;
|
|
4567
|
-
if (pemPath) if (!existsSync(pemPath)) onDiagnostic?.({
|
|
4568
|
-
event: "vm.credentials.github_key_missing",
|
|
4569
|
-
level: "warning",
|
|
4570
|
-
credentialMode: mode,
|
|
4571
|
-
message: `github.private_key_path not found at ${pemPath}; moltnet github token will fail inside the guest`
|
|
4572
|
-
});
|
|
4573
|
-
else {
|
|
4574
|
-
githubAppPem = readFileSync(pemPath, "utf8");
|
|
4575
|
-
githubAppPemFilename = path.basename(pemPath);
|
|
4576
|
-
}
|
|
4577
|
-
return {
|
|
4578
|
-
moltnetJson,
|
|
4579
|
-
agentEnvRaw,
|
|
4580
|
-
piAuthJson,
|
|
4581
|
-
agentEnv: parseEnv(agentEnvRaw),
|
|
4582
|
-
gitconfig,
|
|
4583
|
-
sshPrivateKey,
|
|
4584
|
-
sshPublicKey,
|
|
4585
|
-
allowedSigners,
|
|
4586
|
-
githubAppPem,
|
|
4587
|
-
githubAppPemFilename
|
|
4588
|
-
};
|
|
4589
|
-
}
|
|
4590
|
-
/**
|
|
4591
|
-
* Apply agent env vars to the host process, mirroring `moltnet start`.
|
|
4592
|
-
* Resolves relative paths (e.g. GIT_CONFIG_GLOBAL) against the repo root.
|
|
4593
|
-
*/
|
|
4594
|
-
function activateAgentEnv(agentEnv, repoRoot) {
|
|
4595
|
-
for (const [k, v] of Object.entries(agentEnv)) {
|
|
4596
|
-
if (v === void 0 || v === null || v === "") continue;
|
|
4597
|
-
let resolved = v;
|
|
4598
|
-
if (k === "GIT_CONFIG_GLOBAL" && !path.isAbsolute(v)) resolved = path.join(repoRoot, v);
|
|
4599
|
-
process.env[k] = resolved;
|
|
4600
|
-
}
|
|
4601
|
-
}
|
|
4602
|
-
var BASE_ALLOWED_HOSTS = [
|
|
4603
|
-
"api.openai.com",
|
|
4604
|
-
"*.openai.com",
|
|
4605
|
-
"chat.openai.com",
|
|
4606
|
-
"chatgpt.com",
|
|
4607
|
-
"*.chatgpt.com",
|
|
4608
|
-
"registry.npmjs.org",
|
|
4609
|
-
"github.com",
|
|
4610
|
-
"*.github.com",
|
|
4611
|
-
"*.githubusercontent.com",
|
|
4612
|
-
"proxy.golang.org",
|
|
4613
|
-
"sum.golang.org",
|
|
4614
|
-
"golang.org",
|
|
4615
|
-
"storage.googleapis.com",
|
|
4616
|
-
"*.googlesource.com"
|
|
4617
|
-
];
|
|
4618
|
-
var DEFAULT_MOLTNET_API_URL = "https://api.themolt.net";
|
|
4619
|
-
/**
|
|
4620
|
-
* Host environment names that may intentionally cross into a
|
|
4621
|
-
* host-authenticated guest. This local list is the authority boundary;
|
|
4622
|
-
* server-supplied runtime-profile `requiredEnv` cannot widen it.
|
|
4623
|
-
*/
|
|
4624
|
-
var HOST_AUTHENTICATED_GUEST_ENV_ALLOWLIST = new Set([
|
|
4625
|
-
"ANTHROPIC_API_KEY",
|
|
4626
|
-
"OPENAI_API_KEY",
|
|
4627
|
-
"OPENAI_BASE_URL",
|
|
4628
|
-
"AZURE_OPENAI_API_KEY",
|
|
4629
|
-
"AZURE_OPENAI_ENDPOINT",
|
|
4630
|
-
"AZURE_OPENAI_API_VERSION",
|
|
4631
|
-
"GOOGLE_API_KEY",
|
|
4632
|
-
"GEMINI_API_KEY",
|
|
4633
|
-
"MISTRAL_API_KEY",
|
|
4634
|
-
"GROQ_API_KEY",
|
|
4635
|
-
"OPENROUTER_API_KEY",
|
|
4636
|
-
"XAI_API_KEY",
|
|
4637
|
-
"CEREBRAS_API_KEY",
|
|
4638
|
-
"DEEPSEEK_API_KEY",
|
|
4639
|
-
"OLLAMA_API_KEY",
|
|
4640
|
-
"OLLAMA_BASE_URL",
|
|
4641
|
-
"AWS_ACCESS_KEY_ID",
|
|
4642
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
4643
|
-
"AWS_SESSION_TOKEN",
|
|
4644
|
-
"AWS_REGION",
|
|
4645
|
-
"AWS_DEFAULT_REGION",
|
|
4646
|
-
"GITHUB_TOKEN",
|
|
4647
|
-
"GH_TOKEN",
|
|
4648
|
-
"LINEAR_API_KEY"
|
|
4649
|
-
]);
|
|
4650
|
-
var RESERVED_GUEST_ENVIRONMENT_NAMES = new Set([
|
|
4651
|
-
"PATH",
|
|
4652
|
-
"HOME",
|
|
4653
|
-
"NODE_EXTRA_CA_CERTS",
|
|
4654
|
-
"MOLTNET_GUEST_WORKSPACE",
|
|
4655
|
-
"GIT_SSH",
|
|
4656
|
-
"GIT_SSH_COMMAND",
|
|
4657
|
-
"SSH_AUTH_SOCK"
|
|
4658
|
-
]);
|
|
4659
|
-
function isReservedGuestEnvironmentName(name) {
|
|
4660
|
-
return name.startsWith("MOLTNET_") || name.startsWith("GIT_CONFIG_") || RESERVED_GUEST_ENVIRONMENT_NAMES.has(name);
|
|
4661
|
-
}
|
|
4662
|
-
var GuestEnvironmentBoundaryError = class extends Error {
|
|
4663
|
-
constructor(refusedNames) {
|
|
4664
|
-
super(`Guest credential boundary refuses runtime-controlled environment variables: ${refusedNames.join(", ")}. Remove them from the runtime profile; MoltNet operations use the trusted host-side Agent.`);
|
|
4665
|
-
this.refusedNames = refusedNames;
|
|
4666
|
-
this.name = "GuestEnvironmentBoundaryError";
|
|
4667
|
-
}
|
|
4668
|
-
};
|
|
4669
|
-
function assertGuestEnvironmentBoundary(options) {
|
|
4670
|
-
const refusedForwardEnv = (options.forwardEnv ?? []).filter((name) => isReservedGuestEnvironmentName(name) || options.guestCredentialMode === "host-authenticated" && !HOST_AUTHENTICATED_GUEST_ENV_ALLOWLIST.has(name));
|
|
4671
|
-
const refusedSandboxEnv = Object.keys(options.sandboxEnv ?? {}).filter(isReservedGuestEnvironmentName);
|
|
4672
|
-
const refused = [...new Set([...refusedForwardEnv, ...refusedSandboxEnv])].sort();
|
|
4673
|
-
if (refused.length > 0) throw new GuestEnvironmentBoundaryError(refused);
|
|
4674
|
-
}
|
|
4675
|
-
/** @deprecated Prefer assertGuestEnvironmentBoundary for mode-aware checks. */
|
|
4676
|
-
function assertHostAuthenticatedGuestEnvironment(options) {
|
|
4677
|
-
assertGuestEnvironmentBoundary({
|
|
4678
|
-
guestCredentialMode: "host-authenticated",
|
|
4679
|
-
...options
|
|
4680
|
-
});
|
|
4681
|
-
}
|
|
4682
|
-
/**
|
|
4683
|
-
* Return whether two Gondolin hostname globs can match at least one common
|
|
4684
|
-
* string. Each `*` is an arbitrary substring, so this walks the product of the
|
|
4685
|
-
* two small glob automata instead of relying on exact-string comparisons.
|
|
4686
|
-
*/
|
|
4687
|
-
function hostnamePatternsOverlap(left, right) {
|
|
4688
|
-
const a = left.trim().toLowerCase();
|
|
4689
|
-
const b = right.trim().toLowerCase();
|
|
4690
|
-
if (!a || !b) return false;
|
|
4691
|
-
const pending = [[0, 0]];
|
|
4692
|
-
const visited = /* @__PURE__ */ new Set();
|
|
4693
|
-
while (pending.length > 0) {
|
|
4694
|
-
const next = pending.pop();
|
|
4695
|
-
if (!next) continue;
|
|
4696
|
-
const [aIndex, bIndex] = next;
|
|
4697
|
-
const state = `${aIndex}:${bIndex}`;
|
|
4698
|
-
if (visited.has(state)) continue;
|
|
4699
|
-
visited.add(state);
|
|
4700
|
-
if (aIndex === a.length && bIndex === b.length) return true;
|
|
4701
|
-
const aChar = a[aIndex];
|
|
4702
|
-
const bChar = b[bIndex];
|
|
4703
|
-
if (aChar === "*") pending.push([aIndex + 1, bIndex]);
|
|
4704
|
-
if (bChar === "*") pending.push([aIndex, bIndex + 1]);
|
|
4705
|
-
if (aChar !== void 0 && bChar !== void 0 && (aChar === "*" || bChar === "*" || aChar === bChar)) pending.push([aChar === "*" ? aIndex : aIndex + 1, bChar === "*" ? bIndex : bIndex + 1]);
|
|
4706
|
-
}
|
|
4707
|
-
return false;
|
|
4708
|
-
}
|
|
4709
|
-
function assertInternalHostsDoNotOverlapProtectedHosts(internalHosts, protectedHosts) {
|
|
4710
|
-
for (const internalHost of internalHosts) {
|
|
4711
|
-
const protectedHost = protectedHosts.find((candidate) => hostnamePatternsOverlap(internalHost, candidate));
|
|
4712
|
-
if (protectedHost) throw new Error(`sandbox.network.allowedInternalHosts pattern "${internalHost}" overlaps external-only host pattern "${protectedHost}"`);
|
|
4713
|
-
}
|
|
4714
|
-
}
|
|
4715
|
-
/**
|
|
4716
|
-
* Run a shell command in the guest and throw if it fails. Mirror of
|
|
4717
|
-
* `run()` in `snapshot.ts` for the resume-side hook chain — every
|
|
4718
|
-
* setup step is essential to a healthy session, so a silent non-zero
|
|
4719
|
-
* exit (e.g. a mount that fails into the FUSE write path, or a
|
|
4720
|
-
* consumer-provided resume command that fails to install pnpm) must
|
|
4721
|
-
* surface immediately rather than fall through to cryptic agent
|
|
4722
|
-
* errors later.
|
|
4723
|
-
*/
|
|
4724
|
-
async function vmRun(vm, label, command, signal) {
|
|
4725
|
-
const wrapped = `set -eu\nset -o pipefail\n${command}`;
|
|
4726
|
-
throwIfAborted(signal, `resume step "${label}"`);
|
|
4727
|
-
const r = await vm.exec([
|
|
4728
|
-
"sh",
|
|
4729
|
-
"-c",
|
|
4730
|
-
wrapped
|
|
4731
|
-
], { signal });
|
|
4732
|
-
if (r.exitCode !== 0) {
|
|
4733
|
-
const tail = [r.stderr, r.stdout].filter(Boolean).join("\n").slice(-800);
|
|
4734
|
-
throw new Error(`resume step "${label}" failed (exit ${r.exitCode}):\n${tail}`);
|
|
4735
|
-
}
|
|
4736
|
-
}
|
|
4737
|
-
function nonErrorMessage(err) {
|
|
4738
|
-
if (typeof err === "string") return err;
|
|
4739
|
-
try {
|
|
4740
|
-
return JSON.stringify(err) ?? "unknown error";
|
|
4741
|
-
} catch {
|
|
4742
|
-
return "unknown error";
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
/**
|
|
4746
|
-
* Resume a VM from a checkpoint, inject credentials, configure egress +
|
|
4747
|
-
* TLS. Returns the managed VM handle.
|
|
4748
|
-
*/
|
|
4749
|
-
async function resumeVm(config) {
|
|
4750
|
-
throwIfAborted(config.signal, "VM resume");
|
|
4751
|
-
const agentDir = resolveVmAgentDir(config);
|
|
4752
|
-
const guestWorkspace = path.resolve(config.mountPath);
|
|
4753
|
-
const guestCredentialMode = config.guestCredentialMode ?? "guest-config";
|
|
4754
|
-
if (guestCredentialMode === "guest-config" && !existsSync(agentDir)) throw new Error(`Agent directory not found: ${agentDir}. Run: moltnet register --name ${config.agentName}`);
|
|
4755
|
-
assertGuestEnvironmentBoundary({
|
|
4756
|
-
guestCredentialMode,
|
|
4757
|
-
forwardEnv: config.forwardEnv,
|
|
4758
|
-
sandboxEnv: config.sandboxConfig?.env
|
|
4759
|
-
});
|
|
4760
|
-
config.onDiagnostic?.({
|
|
4761
|
-
event: "vm.credentials.mode",
|
|
4762
|
-
level: "info",
|
|
4763
|
-
credentialMode: guestCredentialMode,
|
|
4764
|
-
message: guestCredentialMode === "host-authenticated" ? "MoltNet agent files and non-allowlisted host environment variables are withheld from the guest" : "The complete MoltNet agent configuration is available to the guest"
|
|
4765
|
-
});
|
|
4766
|
-
const creds = loadCredentials(agentDir, guestCredentialMode, config.onDiagnostic);
|
|
4767
|
-
const configuredApiUrl = creds.moltnetJson ? JSON.parse(creds.moltnetJson).endpoints.api : void 0;
|
|
4768
|
-
const apiHost = new URL(configuredApiUrl ?? process.env.MOLTNET_API_URL ?? DEFAULT_MOLTNET_API_URL).hostname;
|
|
4769
|
-
const runtimeAllowedHosts = config.sandboxConfig?.network?.allowedHosts ?? [];
|
|
4770
|
-
const runtimeAllowedInternalHosts = config.sandboxConfig?.network?.allowedInternalHosts ?? [];
|
|
4771
|
-
const protectedExternalHosts = [...new Set([
|
|
4772
|
-
...BASE_ALLOWED_HOSTS,
|
|
4773
|
-
apiHost,
|
|
4774
|
-
...config.extraAllowedHosts ?? []
|
|
4775
|
-
])];
|
|
4776
|
-
assertInternalHostsDoNotOverlapProtectedHosts(runtimeAllowedInternalHosts, protectedExternalHosts);
|
|
4777
|
-
const { httpHooks, env: secretEnv } = createHttpHooks({
|
|
4778
|
-
allowedHosts: [...new Set([...protectedExternalHosts, ...runtimeAllowedHosts])],
|
|
4779
|
-
allowedInternalHosts: runtimeAllowedInternalHosts
|
|
4780
|
-
});
|
|
4781
|
-
const vmAgentDir = `/home/agent/.moltnet/${config.agentName}`;
|
|
4782
|
-
const vmAgentEnv = {};
|
|
4783
|
-
for (const [k, v] of Object.entries(creds.agentEnv)) {
|
|
4784
|
-
if (v === void 0 || v === "") continue;
|
|
4785
|
-
if (k === "GIT_CONFIG_GLOBAL") vmAgentEnv[k] = `${vmAgentDir}/gitconfig`;
|
|
4786
|
-
else if (k.endsWith("_PRIVATE_KEY_PATH")) vmAgentEnv[k] = `${vmAgentDir}/${path.basename(v)}`;
|
|
4787
|
-
else vmAgentEnv[k] = v;
|
|
4788
|
-
}
|
|
4789
|
-
if (creds.moltnetJson) vmAgentEnv.MOLTNET_CREDENTIALS_PATH = `${vmAgentDir}/moltnet.json`;
|
|
4790
|
-
const vfsConfig = resolveVfsShadowConfig(config.sandboxConfig);
|
|
4791
|
-
let workspaceProvider = new RealFSProvider(config.mountPath);
|
|
4792
|
-
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
4793
|
-
shouldShadow: ({ path: shadowPath }) => shouldShadowNodeModulesPath(shadowPath),
|
|
4794
|
-
denySymlinkBypass: false,
|
|
4795
|
-
tmpfs: new AutoParentMemoryProvider(),
|
|
4796
|
-
writeMode: "tmpfs"
|
|
4797
|
-
});
|
|
4798
|
-
if (vfsConfig.mode !== "none") {
|
|
4799
|
-
const predicate = createShadowPathPredicate(vfsConfig.patterns);
|
|
4800
|
-
workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
4801
|
-
shouldShadow: predicate,
|
|
4802
|
-
writeMode: vfsConfig.mode
|
|
4803
|
-
});
|
|
4804
|
-
}
|
|
4805
|
-
if (guestCredentialMode === "host-authenticated") workspaceProvider = new ShadowProvider(workspaceProvider, {
|
|
4806
|
-
shouldShadow: ({ path: shadowPath }) => shadowPath.split("/").includes(".moltnet"),
|
|
4807
|
-
denySymlinkBypass: true,
|
|
4808
|
-
writeMode: "deny"
|
|
4809
|
-
});
|
|
4810
|
-
const forwardedEnv = {};
|
|
4811
|
-
for (const name of config.forwardEnv ?? []) {
|
|
4812
|
-
const value = process.env[name];
|
|
4813
|
-
if (value === void 0 || value === "") continue;
|
|
4814
|
-
forwardedEnv[name] = value;
|
|
4815
|
-
}
|
|
4816
|
-
const envOverrides = config.sandboxConfig?.env ?? {};
|
|
4817
|
-
const vmEnv = {
|
|
4818
|
-
...secretEnv,
|
|
4819
|
-
...vmAgentEnv,
|
|
4820
|
-
...forwardedEnv,
|
|
4821
|
-
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go/bin",
|
|
4822
|
-
HOME: "/home/agent",
|
|
4823
|
-
NODE_NO_WARNINGS: "1",
|
|
4824
|
-
NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/ca-certificates.crt",
|
|
4825
|
-
...envOverrides,
|
|
4826
|
-
MOLTNET_GUEST_WORKSPACE: guestWorkspace
|
|
4827
|
-
};
|
|
4828
|
-
const resources = config.sandboxConfig?.resources;
|
|
4829
|
-
const workspaceMode = config.workspaceMode ?? "shared_mount";
|
|
4830
|
-
const vm = await abortableResource({
|
|
4831
|
-
promise: VmCheckpoint.load(config.checkpointPath).resume({
|
|
4832
|
-
httpHooks,
|
|
4833
|
-
env: vmEnv,
|
|
4834
|
-
...resources?.memory && { memory: resources.memory },
|
|
4835
|
-
...resources?.cpus && { cpus: resources.cpus },
|
|
4836
|
-
vfs: { mounts: {
|
|
4837
|
-
[guestWorkspace]: workspaceProvider,
|
|
4838
|
-
[GUEST_TASK_CONTEXT_MOUNT]: new MemoryProvider()
|
|
4839
|
-
} }
|
|
4840
|
-
}),
|
|
4841
|
-
signal: config.signal,
|
|
4842
|
-
label: "VM resume",
|
|
4843
|
-
cleanup: (resumedVm) => resumedVm.close(),
|
|
4844
|
-
onCleanupError: (err) => {
|
|
4845
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4846
|
-
process.stderr.write(`[vm] aborted resume late vm.close() failed: ${message}\n`);
|
|
4847
|
-
}
|
|
4848
|
-
});
|
|
4849
|
-
try {
|
|
4850
|
-
await vmRun(vm, "TLS certificates", `
|
|
4851
|
-
cp /etc/gondolin/mitm/ca.crt /usr/local/share/ca-certificates/gondolin-mitm.crt
|
|
4852
|
-
update-ca-certificates 2>/dev/null
|
|
4853
|
-
cat /etc/gondolin/mitm/ca.crt >> /etc/ssl/certs/ca-certificates.crt
|
|
4854
|
-
`, config.signal);
|
|
4855
|
-
await vmRun(vm, "DNS resolvers", `printf 'nameserver 8.8.8.8\\nnameserver 1.1.1.1\\n' > /etc/resolv.conf`, config.signal);
|
|
4856
|
-
await vmRun(vm, "git safe.directory", `git config --system --add safe.directory '*'`, config.signal);
|
|
4857
|
-
for (const [i, entry] of (config.sandboxConfig?.resumeCommands ?? []).entries()) {
|
|
4858
|
-
if (!shouldRunResumeCommand(entry, { workspaceMode })) continue;
|
|
4859
|
-
const { run, retries, backoffMs } = typeof entry === "string" ? {
|
|
4860
|
-
run: entry,
|
|
4861
|
-
retries: 0,
|
|
4862
|
-
backoffMs: 2e3
|
|
4863
|
-
} : {
|
|
4864
|
-
run: entry.run,
|
|
4865
|
-
retries: entry.retries ?? 0,
|
|
4866
|
-
backoffMs: entry.retryBackoffMs ?? 2e3
|
|
4867
|
-
};
|
|
4868
|
-
const label = `resumeCommands[${i}]`;
|
|
4869
|
-
let lastErr;
|
|
4870
|
-
for (let attempt = 0; attempt <= retries; attempt++) try {
|
|
4871
|
-
await vmRun(vm, label, run, config.signal);
|
|
4872
|
-
lastErr = void 0;
|
|
4873
|
-
break;
|
|
4874
|
-
} catch (err) {
|
|
4875
|
-
lastErr = err;
|
|
4876
|
-
if (attempt === retries) break;
|
|
4877
|
-
await delay((attempt + 1) * backoffMs, config.signal, label);
|
|
4878
|
-
}
|
|
4879
|
-
if (lastErr) throw lastErr instanceof Error ? lastErr : new Error(nonErrorMessage(lastErr));
|
|
4880
|
-
}
|
|
4881
|
-
const vmSshDir = `${vmAgentDir}/ssh`;
|
|
4882
|
-
const hasAgentFiles = guestCredentialMode === "guest-config";
|
|
4883
|
-
await vm.exec(hasAgentFiles ? `mkdir -p ${vmAgentDir}/ssh /home/agent/.pi/agent` : "mkdir -p /home/agent/.pi/agent", { signal: config.signal });
|
|
4884
|
-
if (creds.piAuthJson !== null) await vm.fs.writeFile("/home/agent/.pi/agent/auth.json", creds.piAuthJson, {
|
|
4885
|
-
mode: 384,
|
|
4886
|
-
signal: config.signal
|
|
4887
|
-
});
|
|
4888
|
-
if (hasAgentFiles) {
|
|
4889
|
-
const vmMoltnetJson = rewriteMoltnetJsonPaths(creds.moltnetJson, vmAgentDir, vmSshDir, creds.githubAppPemFilename);
|
|
4890
|
-
await vm.fs.writeFile(`${vmAgentDir}/moltnet.json`, vmMoltnetJson, {
|
|
4891
|
-
mode: 384,
|
|
4892
|
-
signal: config.signal
|
|
4893
|
-
});
|
|
4894
|
-
await vm.fs.writeFile(`${vmAgentDir}/env`, creds.agentEnvRaw, {
|
|
4895
|
-
mode: 384,
|
|
4896
|
-
signal: config.signal
|
|
4897
|
-
});
|
|
4898
|
-
if (creds.gitconfig) {
|
|
4899
|
-
const vmGitconfig = rewriteGitconfigPaths(creds.gitconfig, vmSshDir, vmAgentDir);
|
|
4900
|
-
await vm.fs.writeFile(`${vmAgentDir}/gitconfig`, vmGitconfig, {
|
|
4901
|
-
mode: 420,
|
|
4902
|
-
signal: config.signal
|
|
4903
|
-
});
|
|
4904
|
-
}
|
|
4905
|
-
if (creds.sshPrivateKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519`, creds.sshPrivateKey, {
|
|
4906
|
-
mode: 384,
|
|
4907
|
-
signal: config.signal
|
|
4908
|
-
});
|
|
4909
|
-
if (creds.sshPublicKey) await vm.fs.writeFile(`${vmSshDir}/id_ed25519.pub`, creds.sshPublicKey, {
|
|
4910
|
-
mode: 420,
|
|
4911
|
-
signal: config.signal
|
|
4912
|
-
});
|
|
4913
|
-
if (creds.allowedSigners) await vm.fs.writeFile(`${vmSshDir}/allowed_signers`, creds.allowedSigners, {
|
|
4914
|
-
mode: 420,
|
|
4915
|
-
signal: config.signal
|
|
4916
|
-
});
|
|
4917
|
-
if (creds.githubAppPem && creds.githubAppPemFilename) await vm.fs.writeFile(`${vmAgentDir}/${creds.githubAppPemFilename}`, creds.githubAppPem, {
|
|
4918
|
-
mode: 384,
|
|
4919
|
-
signal: config.signal
|
|
4920
|
-
});
|
|
4921
|
-
}
|
|
4922
|
-
await vm.exec(hasAgentFiles ? "chown -R agent:agent /home/agent/.pi /home/agent/.moltnet" : "chown -R agent:agent /home/agent/.pi", { signal: config.signal });
|
|
4923
|
-
return {
|
|
4924
|
-
vm,
|
|
4925
|
-
credentials: creds,
|
|
4926
|
-
mountPath: config.mountPath,
|
|
4927
|
-
guestWorkspace,
|
|
4928
|
-
agentDir
|
|
4929
|
-
};
|
|
4930
|
-
} catch (err) {
|
|
4931
|
-
try {
|
|
4932
|
-
await vm.close();
|
|
4933
|
-
} catch (closeErr) {
|
|
4934
|
-
const m = closeErr instanceof Error ? closeErr.message : String(closeErr);
|
|
4935
|
-
process.stderr.write(`[vm] post-throw vm.close() failed: ${m}\n`);
|
|
4936
|
-
}
|
|
4937
|
-
throw err;
|
|
4938
|
-
}
|
|
4939
|
-
}
|
|
4940
|
-
/**
|
|
4941
|
-
* Rewrite host-absolute paths inside an agent gitconfig to VM-local
|
|
4942
|
-
* equivalents before injecting it into the guest.
|
|
4943
|
-
*
|
|
4944
|
-
* Two rewrites:
|
|
4945
|
-
* - `signingKey = <host path>` → `<vmSshDir>/id_ed25519`
|
|
4946
|
-
* - `... credential-helper --credentials <host moltnet.json>`
|
|
4947
|
-
* → `<vmAgentDir>/moltnet.json`
|
|
4948
|
-
*
|
|
4949
|
-
* The credential-helper line is generated host-side by `moltnet github setup`
|
|
4950
|
-
* with a host-absolute `--credentials` path; inside the guest that path is
|
|
4951
|
-
* invalid, so it must point at the VM-side moltnet.json. The `insteadOf`
|
|
4952
|
-
* rewrite rule and every other line are workspace-independent and pass through
|
|
4953
|
-
* unchanged. A gitconfig without a credential helper is rewritten only for
|
|
4954
|
-
* `signingKey`.
|
|
4955
|
-
*
|
|
4956
|
-
* This is the single source of truth for git push auth in the guest: the
|
|
4957
|
-
* injected gitconfig carries the tokenless mint-on-demand helper, so the VM
|
|
4958
|
-
* no longer hand-rolls a credential-helper script or runs an imperative
|
|
4959
|
-
* `git config --global ... insteadOf` against the guest $HOME.
|
|
4960
|
-
*/
|
|
4961
|
-
function rewriteGitconfigPaths(gitconfig, vmSshDir, vmAgentDir) {
|
|
4962
|
-
return gitconfig.replace(/signingKey\s*=\s*.+/g, `signingKey = ${vmSshDir}/id_ed25519`).replace(/(moltnet github credential-helper --credentials )\S+/g, `$1${vmAgentDir}/moltnet.json`);
|
|
4963
|
-
}
|
|
4964
|
-
/**
|
|
4965
|
-
* Rewrite host-absolute paths inside moltnet.json to VM-local equivalents.
|
|
4966
|
-
*
|
|
4967
|
-
* Fields rewritten:
|
|
4968
|
-
* ssh.private_key_path → <vmSshDir>/<basename of original>
|
|
4969
|
-
* ssh.public_key_path → <vmSshDir>/<basename of original>
|
|
4970
|
-
* git.config_path → <vmAgentDir>/gitconfig
|
|
4971
|
-
* github.private_key_path → <vmAgentDir>/<pemFilename> (if present)
|
|
4972
|
-
*
|
|
4973
|
-
* All other fields are passed through unchanged.
|
|
4974
|
-
* Throws if moltnetJson is not valid JSON — callers must not inject a broken
|
|
4975
|
-
* moltnet.json into the guest.
|
|
4976
|
-
*/
|
|
4977
|
-
function rewriteMoltnetJsonPaths(moltnetJson, vmAgentDir, vmSshDir, githubAppPemFilename) {
|
|
4978
|
-
const config = JSON.parse(moltnetJson);
|
|
4979
|
-
if (config.ssh && typeof config.ssh === "object") {
|
|
4980
|
-
const ssh = config.ssh;
|
|
4981
|
-
const origPrivate = typeof ssh.private_key_path === "string" ? ssh.private_key_path : null;
|
|
4982
|
-
const origPublic = typeof ssh.public_key_path === "string" ? ssh.public_key_path : null;
|
|
4983
|
-
config.ssh = {
|
|
4984
|
-
...ssh,
|
|
4985
|
-
...origPrivate !== null && { private_key_path: `${vmSshDir}/${path.basename(origPrivate)}` },
|
|
4986
|
-
...origPublic !== null && { public_key_path: `${vmSshDir}/${path.basename(origPublic)}` }
|
|
4987
|
-
};
|
|
4988
|
-
}
|
|
4989
|
-
if (config.git && typeof config.git === "object") {
|
|
4990
|
-
const git = { ...config.git };
|
|
4991
|
-
git.config_path = `${vmAgentDir}/gitconfig`;
|
|
4992
|
-
config.git = git;
|
|
4993
|
-
}
|
|
4994
|
-
if (githubAppPemFilename && config.github && typeof config.github === "object") {
|
|
4995
|
-
const github = { ...config.github };
|
|
4996
|
-
github.private_key_path = `${vmAgentDir}/${githubAppPemFilename}`;
|
|
4997
|
-
config.github = github;
|
|
4998
|
-
}
|
|
4999
|
-
return JSON.stringify(config);
|
|
5000
|
-
}
|
|
5001
|
-
//#endregion
|
|
5002
3185
|
//#region src/tool-operations.ts
|
|
5003
3186
|
/**
|
|
5004
3187
|
* Gondolin tool operations: redirect pi's built-in tool operations
|
|
@@ -5701,6 +3884,34 @@ function decisionContext(deps) {
|
|
|
5701
3884
|
};
|
|
5702
3885
|
}
|
|
5703
3886
|
//#endregion
|
|
3887
|
+
//#region src/vm.ts
|
|
3888
|
+
/** Guest path where Pi expects its auth blob. */
|
|
3889
|
+
var PI_GUEST_AUTH_PATH = "/home/agent/.pi/agent/auth.json";
|
|
3890
|
+
/**
|
|
3891
|
+
* Pi's provider authentication as a sandbox `ProviderAuthSource`. CI writes
|
|
3892
|
+
* `auth.json` under `PI_CODING_AGENT_DIR`; local runs fall back to the
|
|
3893
|
+
* canonical `~/.pi/agent` dir when the override is unset.
|
|
3894
|
+
*/
|
|
3895
|
+
function piProviderAuth() {
|
|
3896
|
+
return {
|
|
3897
|
+
guestPath: PI_GUEST_AUTH_PATH,
|
|
3898
|
+
load: () => {
|
|
3899
|
+
const authPath = path.join(resolvePiCodingAgentDir(), "auth.json");
|
|
3900
|
+
return existsSync(authPath) ? readFileSync(authPath, "utf8") : null;
|
|
3901
|
+
}
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Resume a Gondolin VM for a Pi session. Identical to the sandbox package's
|
|
3906
|
+
* `resumeVm`, with Pi's provider auth supplied unless the caller overrides it.
|
|
3907
|
+
*/
|
|
3908
|
+
function resumeVm(config) {
|
|
3909
|
+
return resumeVm$1({
|
|
3910
|
+
providerAuth: piProviderAuth(),
|
|
3911
|
+
...config
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
//#endregion
|
|
5704
3915
|
//#region src/runtime/capability-discovery.ts
|
|
5705
3916
|
var GuestExecutableProbeError = class extends Error {
|
|
5706
3917
|
code;
|
|
@@ -6183,7 +4394,12 @@ function buildSandboxCapabilityInstructions(sandbox, policy) {
|
|
|
6183
4394
|
];
|
|
6184
4395
|
const externalHosts = [...sandbox.allowedHosts].sort();
|
|
6185
4396
|
const internalHosts = [...sandbox.allowedInternalHosts].sort();
|
|
6186
|
-
|
|
4397
|
+
const brokeredSecretEnvNames = [...sandbox.brokeredSecretEnvNames ?? []].sort();
|
|
4398
|
+
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(", ")}.`] : [], ...brokeredSecretEnvNames.length > 0 ? [
|
|
4399
|
+
"- Host-brokered HTTP credentials are available only as opaque",
|
|
4400
|
+
` placeholders in: ${brokeredSecretEnvNames.map((name) => `\`${name}\``).join(", ")}. The host proxy may substitute them only for their`,
|
|
4401
|
+
" declared destination hosts. Do not print, persist, or move them."
|
|
4402
|
+
] : [], "- Runtime service endpoints required for task execution may be available", " in addition to the operator-configured hosts above.");
|
|
6187
4403
|
return lines.join("\n");
|
|
6188
4404
|
}
|
|
6189
4405
|
function shellExecutableIsAvailable(policy, sandbox, executable) {
|
|
@@ -6196,20 +4412,27 @@ function buildCredentialInstructions(policy, sandbox) {
|
|
|
6196
4412
|
const lines = [
|
|
6197
4413
|
"## Identity & credentials",
|
|
6198
4414
|
"",
|
|
6199
|
-
"-
|
|
6200
|
-
"
|
|
6201
|
-
"
|
|
4415
|
+
"- Long-lived MoltNet identity and signing credentials remain on the",
|
|
4416
|
+
" trusted daemon host. Guest credential files, SSH signing keys, GitHub",
|
|
4417
|
+
" App private keys, and credential helpers are not supported guest",
|
|
4418
|
+
" capabilities. Do not inspect or use them even if transitional",
|
|
4419
|
+
" compatibility plumbing makes one visible.",
|
|
4420
|
+
"- Use structured `moltnet_*` tools for authenticated MoltNet operations.",
|
|
4421
|
+
" Do not try to recover host configuration through shell commands."
|
|
6202
4422
|
];
|
|
6203
4423
|
const moltnetAvailable = shellExecutableIsAvailable(policy, sandbox, "moltnet");
|
|
6204
4424
|
const ghAvailable = shellExecutableIsAvailable(policy, sandbox, "gh");
|
|
6205
4425
|
const gitAvailable = shellExecutableIsAvailable(policy, sandbox, "git");
|
|
6206
|
-
if (moltnetAvailable) lines.push("-
|
|
4426
|
+
if (moltnetAvailable) lines.push("- The installed `moltnet` binary has no guest identity credentials;", " do not use it for authenticated operations.");
|
|
6207
4427
|
if (ghAvailable) {
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
4428
|
+
const githubPlaceholder = (sandbox?.brokeredSecretEnvNames ?? []).find((name) => name === "GH_TOKEN" || name === "GITHUB_TOKEN");
|
|
4429
|
+
lines.push(...githubPlaceholder ? [
|
|
4430
|
+
`- GitHub CLI authentication uses the host-brokered \`${githubPlaceholder}\``,
|
|
4431
|
+
" placeholder. Use it normally for policy-authorized HTTPS",
|
|
4432
|
+
" requests; it is not a reusable or inspectable token."
|
|
4433
|
+
] : ["- No brokered GitHub credential is active. Authenticated `gh`", " operations are unavailable; do not mint or recover a host token."]);
|
|
6211
4434
|
}
|
|
6212
|
-
if (gitAvailable) lines.push("-
|
|
4435
|
+
if (gitAvailable) lines.push("- Local Git commands run inside the guest. No signing key or Git", " credential helper is injected; signing and authenticated push", " require an explicitly provided capability.");
|
|
6213
4436
|
return lines.join("\n");
|
|
6214
4437
|
}
|
|
6215
4438
|
/**
|
|
@@ -7147,7 +5370,7 @@ function gitRefExists(mainRepo, ref) {
|
|
|
7147
5370
|
}
|
|
7148
5371
|
function findMainWorktreeForDedicatedTask(startPath) {
|
|
7149
5372
|
try {
|
|
7150
|
-
return findMainWorktree(startPath);
|
|
5373
|
+
return findMainWorktree$1(startPath);
|
|
7151
5374
|
} catch (err) {
|
|
7152
5375
|
const message = err instanceof Error ? err.message : String(err);
|
|
7153
5376
|
throw new Error(`Dedicated worktree tasks require a git repository: ${message}`);
|
|
@@ -7286,6 +5509,20 @@ function createGondolinToolDefinitions(config) {
|
|
|
7286
5509
|
}
|
|
7287
5510
|
];
|
|
7288
5511
|
}
|
|
5512
|
+
/** Resolve one attempt's host-only HTTP credentials before VM resume. */
|
|
5513
|
+
async function resolveAttemptBrokeredHttpSecrets(input) {
|
|
5514
|
+
if (!input.runtimeDefinition) return void 0;
|
|
5515
|
+
return materializePiBrokeredHttpSecrets({
|
|
5516
|
+
runtime: input.runtimeDefinition,
|
|
5517
|
+
context: {
|
|
5518
|
+
agentName: input.agentName,
|
|
5519
|
+
claimedTask: input.claimedTask,
|
|
5520
|
+
cwdPath: input.cwdPath
|
|
5521
|
+
},
|
|
5522
|
+
signal: input.signal,
|
|
5523
|
+
timeoutMs: input.timeoutMs
|
|
5524
|
+
});
|
|
5525
|
+
}
|
|
7289
5526
|
function createMoltNetAgentResolver(input) {
|
|
7290
5527
|
let resolved;
|
|
7291
5528
|
return () => {
|
|
@@ -7317,7 +5554,7 @@ function createPiTaskExecutor(opts) {
|
|
|
7317
5554
|
if (!cachedCheckpoint) if (opts.runtimeDefinition) {
|
|
7318
5555
|
cachedTemplate = await opts.runtimeDefinition.vm.resolve({ onProgress: opts.onSnapshotProgress });
|
|
7319
5556
|
cachedCheckpoint = cachedTemplate.checkpointPath;
|
|
7320
|
-
} else cachedCheckpoint = await ensureSnapshot({
|
|
5557
|
+
} else cachedCheckpoint = await ensureSnapshot$1({
|
|
7321
5558
|
config: opts.sandboxConfig?.snapshot,
|
|
7322
5559
|
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
7323
5560
|
process.stderr.write(`[snapshot] ${m}\n`);
|
|
@@ -7441,10 +5678,10 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
7441
5678
|
reporterOpen = true;
|
|
7442
5679
|
let checkpointPath;
|
|
7443
5680
|
let resolvedVmTemplate = opts.resolvedVmTemplate;
|
|
7444
|
-
let
|
|
5681
|
+
let brokeredSecretEnvNames = [];
|
|
7445
5682
|
try {
|
|
7446
5683
|
if (!resolvedVmTemplate && opts.runtimeDefinition) resolvedVmTemplate = opts.resolveVmTemplate ? await opts.resolveVmTemplate() : await opts.runtimeDefinition.vm.resolve({ onProgress: opts.onSnapshotProgress });
|
|
7447
|
-
checkpointPath = await traceRuntimePhase("moltnet.execution.snapshot.prepare", { "moltnet.snapshot.source": resolvedVmTemplate ? "resolved_template" : opts.checkpointPath ? "configured_checkpoint" : opts.resolveCheckpointPath ? "resolver" : "build_or_cache" }, async () => resolvedVmTemplate?.checkpointPath ?? opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
|
|
5684
|
+
checkpointPath = await traceRuntimePhase("moltnet.execution.snapshot.prepare", { "moltnet.snapshot.source": resolvedVmTemplate ? "resolved_template" : opts.checkpointPath ? "configured_checkpoint" : opts.resolveCheckpointPath ? "resolver" : "build_or_cache" }, async () => resolvedVmTemplate?.checkpointPath ?? opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot$1({
|
|
7448
5685
|
config: opts.sandboxConfig?.snapshot,
|
|
7449
5686
|
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
7450
5687
|
process.stderr.write(`[snapshot] ${m}\n`);
|
|
@@ -7469,13 +5706,33 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
7469
5706
|
}
|
|
7470
5707
|
if (!workspace) throw new Error("task workspace not prepared");
|
|
7471
5708
|
const preparedWorkspace = workspace;
|
|
5709
|
+
const effectiveSandboxConfig = applyExecutionPlanSandboxOverrides(resolvedVmTemplate ? {
|
|
5710
|
+
...opts.sandboxConfig,
|
|
5711
|
+
snapshot: void 0,
|
|
5712
|
+
resumeCommands: [...resolvedVmTemplate.resumeCommands]
|
|
5713
|
+
} : opts.sandboxConfig, executionPlan);
|
|
5714
|
+
let brokeredSecrets;
|
|
5715
|
+
try {
|
|
5716
|
+
const runtimeDefinition = opts.runtimeDefinition;
|
|
5717
|
+
brokeredSecrets = runtimeDefinition ? await traceRuntimePhase("moltnet.execution.credentials.resolve", { "moltnet.credentials.requirement_count": runtimeDefinition.brokeredHttpSecrets?.length ?? 0 }, () => resolveAttemptBrokeredHttpSecrets({
|
|
5718
|
+
runtimeDefinition,
|
|
5719
|
+
agentName: opts.agentName,
|
|
5720
|
+
claimedTask,
|
|
5721
|
+
cwdPath,
|
|
5722
|
+
signal: reporter.cancelSignal
|
|
5723
|
+
})) : void 0;
|
|
5724
|
+
} catch (err) {
|
|
5725
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5726
|
+
if (reporter.cancelSignal.aborted || err?.name === "AbortError") {
|
|
5727
|
+
await emitError("credential_resolution", message, { cancelled: true });
|
|
5728
|
+
return makeCancelledOutput(reporter.cancelReason ?? "Task cancelled during credential resolution.");
|
|
5729
|
+
}
|
|
5730
|
+
await emitError("credential_resolution", message);
|
|
5731
|
+
return makeFailedOutput("credential_resolution_failed", message, finalUsage, err instanceof PiBrokeredHttpSecretResolutionError ? err.retryable : false);
|
|
5732
|
+
}
|
|
7472
5733
|
try {
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
snapshot: void 0,
|
|
7476
|
-
resumeCommands: [...resolvedVmTemplate.resumeCommands]
|
|
7477
|
-
} : opts.sandboxConfig, executionPlan);
|
|
7478
|
-
managed = await traceRuntimePhase("moltnet.execution.vm.resume", { "moltnet.workspace.mode": preparedWorkspace.mode }, () => resumeVm({
|
|
5734
|
+
brokeredSecretEnvNames = (brokeredSecrets ?? []).filter(({ value }) => value !== void 0 && value !== "").map(({ guestEnv }) => guestEnv).sort();
|
|
5735
|
+
managed = await traceRuntimePhase("moltnet.execution.vm.resume", { "moltnet.workspace.mode": preparedWorkspace.mode }, () => (opts.resumeVm ?? resumeVm)({
|
|
7479
5736
|
checkpointPath,
|
|
7480
5737
|
agentName: opts.agentName,
|
|
7481
5738
|
agentRootDir,
|
|
@@ -7485,6 +5742,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
7485
5742
|
extraAllowedHosts: opts.extraAllowedHosts,
|
|
7486
5743
|
sandboxConfig: effectiveSandboxConfig,
|
|
7487
5744
|
forwardEnv: opts.forwardEnv,
|
|
5745
|
+
brokeredSecrets,
|
|
7488
5746
|
onDiagnostic: opts.onVmDiagnostic,
|
|
7489
5747
|
signal: reporter.cancelSignal
|
|
7490
5748
|
}));
|
|
@@ -7499,7 +5757,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
7499
5757
|
}
|
|
7500
5758
|
const diaryId = task.diaryId ?? "";
|
|
7501
5759
|
const taskTeamId = task.teamId ?? "";
|
|
7502
|
-
activateAgentEnv(managed.credentials.agentEnv, agentRootDir);
|
|
5760
|
+
activateAgentEnv$1(managed.credentials.agentEnv, agentRootDir);
|
|
7503
5761
|
const activeWorkspace = preparedWorkspace;
|
|
7504
5762
|
const activeManaged = managed;
|
|
7505
5763
|
const getMoltNetAgent = createMoltNetAgentResolver({
|
|
@@ -7792,7 +6050,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
7792
6050
|
nodeModulesWriteMode: "tmpfs",
|
|
7793
6051
|
verifiedExecutables: verifiedGuestExecutables,
|
|
7794
6052
|
allowedHosts: [...effectiveSandboxConfig?.network?.allowedHosts ?? [], ...opts.extraAllowedHosts ?? []],
|
|
7795
|
-
allowedInternalHosts: effectiveSandboxConfig?.network?.allowedInternalHosts ?? []
|
|
6053
|
+
allowedInternalHosts: effectiveSandboxConfig?.network?.allowedInternalHosts ?? [],
|
|
6054
|
+
brokeredSecretEnvNames
|
|
7796
6055
|
},
|
|
7797
6056
|
toolPolicy: capabilityProjection.instructorPolicy
|
|
7798
6057
|
});
|
|
@@ -8765,4 +7024,4 @@ function describeToolErrorMessage(result) {
|
|
|
8765
7024
|
}
|
|
8766
7025
|
}
|
|
8767
7026
|
//#endregion
|
|
8768
|
-
export { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, GuestEnvironmentBoundaryError, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION, PI_RUNTIME_DEFINITION_VERSION, activateAgentEnv, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, buildAgentSession, buildPiExecutorManifest, buildRuntimeKernel, buildWorkspaceMountInstructions, createGondolinBashOps, createGondolinEditOps, createGondolinFindOps, createGondolinLsOps, createGondolinReadOps, createGondolinToolDefinitions, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, createToolPolicyExtension, decideForEvent, decideToolCall, defineGondolinTemplate, definePiExtension, definePiRuntime, definePiTool, enabledPiToolNames, ensureSnapshot, executeGondolinGrep, executePiTask, filterModelVisibleTools, findMainWorktree, injectRuntimeContext as injectTaskContext, isKernelTool, isResolvedPathInsideRoot, isToolVisible, loadCredentials, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveHostExecBaseEnv, resolveSessionToolPolicy, resolveTaskWorktreePath, resumeVm, toGuestPath };
|
|
7027
|
+
export { BrokeredHttpSecretBoundaryError, DEFAULT_BROKERED_HTTP_SECRET_RESOLUTION_TIMEOUT_MS, GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, GuestEnvironmentBoundaryError, HOST_EXEC_DEFAULT_BASE_ENV, MOLTNET_TOOL_NAMES, PI_EXECUTOR_MANIFEST_VERSION, PI_GUEST_AUTH_PATH, PI_RUNTIME_DEFINITION_VERSION, PiBrokeredHttpSecretResolutionError, activateAgentEnv, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, buildAgentSession, buildPiExecutorManifest, buildRuntimeKernel, buildWorkspaceMountInstructions, createGondolinBashOps, createGondolinEditOps, createGondolinFindOps, createGondolinLsOps, createGondolinReadOps, createGondolinToolDefinitions, createGondolinWriteOps, createMoltNetTools, createPiOtelExtension, createPiRetryTriage, createPiTaskExecutor, createSubagentTool, createToolPolicyExtension, decideForEvent, decideToolCall, defineGondolinTemplate, definePiBrokeredHttpSecret, definePiExtension, definePiRuntime, definePiTool, enabledPiToolNames, ensureSnapshot, executeGondolinGrep, executePiTask, filterModelVisibleTools, findMainWorktree, injectRuntimeContext as injectTaskContext, isKernelTool, isResolvedPathInsideRoot, isToolVisible, loadCredentials, materializePiBrokeredHttpSecrets, materializePiExtensions, materializePiTools, normalizeRetryTriageResult, piProviderAuth, prepareBrokeredHttpSecrets, redactRetryTriageSecrets, resolveHostExecBaseEnv, resolveSessionToolPolicy, resolveTaskWorktreePath, resumeVm, toGuestPath };
|