@themoltnet/agent-daemon 0.7.0 → 0.9.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/README.md +36 -1
- package/dist/main.js +422 -176
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ export ANTHROPIC_API_KEY=sk-ant-...
|
|
|
57
57
|
# https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/env-api-keys.ts
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
To use
|
|
60
|
+
To use a non-default auth directory: `PI_CODING_AGENT_DIR=/abs/path/to/.pi/agent`.
|
|
61
61
|
|
|
62
62
|
### Observability
|
|
63
63
|
|
|
@@ -254,6 +254,41 @@ moltnet entry list --diary-id "$MOLTNET_DIARY_ID" --limit 10 \
|
|
|
254
254
|
--credentials "$PWD/.moltnet/local-dev/moltnet.json"
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
+
### 4b. Create a `pr_review` smoke task
|
|
258
|
+
|
|
259
|
+
Use this path when you want to exercise the generic `pr_review` task type
|
|
260
|
+
against the local e2e stack before the new schema exists on a deployed API.
|
|
261
|
+
|
|
262
|
+
Start the daemon with `--task-types pr_review`:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
pnpm --filter @themoltnet/agent-daemon dev poll \
|
|
266
|
+
--agent local-dev \
|
|
267
|
+
--team "$MOLTNET_TEAM_ID" \
|
|
268
|
+
--task-types pr_review \
|
|
269
|
+
--provider openai-codex \
|
|
270
|
+
--model gpt-5.4-codex \
|
|
271
|
+
--debug
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Then, in another terminal, create the task:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
pnpm exec tsx tools/src/tasks/create-pr-review.ts \
|
|
278
|
+
--agent local-dev \
|
|
279
|
+
--pr <number> \
|
|
280
|
+
--repo <owner/repo>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
This helper stays imposer-only. It reads PR metadata, ensures the PR
|
|
284
|
+
correlation marker exists, loads the binary rubric, and creates the
|
|
285
|
+
`pr_review` task. The daemon-claimed LLM attempt remains responsible for
|
|
286
|
+
the review itself and for any requested outward action such as `gh pr comment`.
|
|
287
|
+
|
|
288
|
+
If you want an automated local check without real GitHub mutation, use the
|
|
289
|
+
stubbed `pr_review` lifecycle coverage in
|
|
290
|
+
`apps/agent-daemon/e2e/daemon.e2e.test.ts` instead of this manual smoke path.
|
|
291
|
+
|
|
257
292
|
### What to verify
|
|
258
293
|
|
|
259
294
|
After the task completes, every entry produced **during the attempt** should:
|
package/dist/main.js
CHANGED
|
@@ -3045,10 +3045,10 @@ function validateRubricWeights(rubric) {
|
|
|
3045
3045
|
* complementary places.
|
|
3046
3046
|
*
|
|
3047
3047
|
* Before this envelope existed, criteria were scattered: a vestigial
|
|
3048
|
-
* `criteriaCid` column nobody resolved,
|
|
3049
|
-
*
|
|
3050
|
-
*
|
|
3051
|
-
*
|
|
3048
|
+
* `criteriaCid` column nobody resolved, free-form prose on
|
|
3049
|
+
* `fulfill_brief.input`, and inline `rubric` / `criteria[]` fields on
|
|
3050
|
+
* judgment-task inputs. None of those were machine-verifiable
|
|
3051
|
+
* end-to-end.
|
|
3052
3052
|
*
|
|
3053
3053
|
* This module defines a single, content-addressable envelope an imposer
|
|
3054
3054
|
* attaches to any task type. It has four orthogonal sections — pick
|
|
@@ -4185,7 +4185,6 @@ var FULFILL_BRIEF_TYPE = "fulfill_brief";
|
|
|
4185
4185
|
var FulfillBriefInput = Type$2.Object({
|
|
4186
4186
|
brief: Type$2.String({ minLength: 1 }),
|
|
4187
4187
|
title: Type$2.Optional(Type$2.String()),
|
|
4188
|
-
acceptanceCriteria: Type$2.Optional(Type$2.Array(Type$2.String())),
|
|
4189
4188
|
successCriteria: Type$2.Optional(SuccessCriteria),
|
|
4190
4189
|
seedFiles: Type$2.Optional(Type$2.Array(Type$2.String())),
|
|
4191
4190
|
scopeHint: Type$2.Optional(Type$2.String())
|
|
@@ -4609,6 +4608,72 @@ async function onCreateJudgeEvalVariant(input, ctx) {
|
|
|
4609
4608
|
}];
|
|
4610
4609
|
}
|
|
4611
4610
|
//#endregion
|
|
4611
|
+
//#region ../../libs/tasks/src/task-types/pr-review.ts
|
|
4612
|
+
var PR_REVIEW_TYPE = "pr_review";
|
|
4613
|
+
var PrReviewSubject = Type$2.Object({
|
|
4614
|
+
title: Type$2.String({ minLength: 1 }),
|
|
4615
|
+
summary: Type$2.String({ minLength: 1 }),
|
|
4616
|
+
resourceUrls: Type$2.Optional(Type$2.Array(Type$2.String({ minLength: 1 }))),
|
|
4617
|
+
inspectionHints: Type$2.Optional(Type$2.Array(Type$2.String({ minLength: 1 })))
|
|
4618
|
+
}, {
|
|
4619
|
+
$id: "PrReviewSubject",
|
|
4620
|
+
additionalProperties: false
|
|
4621
|
+
});
|
|
4622
|
+
var PrReviewInput = Type$2.Object({
|
|
4623
|
+
subject: PrReviewSubject,
|
|
4624
|
+
taskPrompt: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4625
|
+
successCriteria: SuccessCriteria
|
|
4626
|
+
}, {
|
|
4627
|
+
$id: "PrReviewInput",
|
|
4628
|
+
additionalProperties: false
|
|
4629
|
+
});
|
|
4630
|
+
var PrReviewScore = Type$2.Object({
|
|
4631
|
+
criterionId: Type$2.String({ minLength: 1 }),
|
|
4632
|
+
score: Type$2.Union([Type$2.Literal(0), Type$2.Literal(1)]),
|
|
4633
|
+
rationale: Type$2.String({ minLength: 1 })
|
|
4634
|
+
}, {
|
|
4635
|
+
$id: "PrReviewScore",
|
|
4636
|
+
additionalProperties: false
|
|
4637
|
+
});
|
|
4638
|
+
var PrReviewOutput = Type$2.Object({
|
|
4639
|
+
scores: Type$2.Array(PrReviewScore, { minItems: 1 }),
|
|
4640
|
+
composite: Type$2.Number({
|
|
4641
|
+
minimum: 0,
|
|
4642
|
+
maximum: 1
|
|
4643
|
+
}),
|
|
4644
|
+
verdict: Type$2.String({ minLength: 1 })
|
|
4645
|
+
}, {
|
|
4646
|
+
$id: "PrReviewOutput",
|
|
4647
|
+
additionalProperties: false
|
|
4648
|
+
});
|
|
4649
|
+
function requireBooleanRubric(rubric) {
|
|
4650
|
+
for (const criterion of rubric.criteria) if (criterion.scoring !== "boolean") return `pr_review requires boolean scoring for every rubric criterion; criterion "${criterion.id}" uses "${criterion.scoring}"`;
|
|
4651
|
+
return null;
|
|
4652
|
+
}
|
|
4653
|
+
function validatePrReviewInput(input) {
|
|
4654
|
+
const sc = input.successCriteria;
|
|
4655
|
+
if (!sc) return "successCriteria is required for judgment tasks";
|
|
4656
|
+
if (!sc.rubric) return "successCriteria.rubric is required for judgment tasks";
|
|
4657
|
+
return validateRubricWeights(sc.rubric) ?? requireBooleanRubric(sc.rubric);
|
|
4658
|
+
}
|
|
4659
|
+
function validatePrReviewOutput(output, input) {
|
|
4660
|
+
if (!input) return null;
|
|
4661
|
+
const scores = output.scores;
|
|
4662
|
+
const rubric = input.successCriteria.rubric;
|
|
4663
|
+
if (!rubric) return null;
|
|
4664
|
+
if (scores.length !== rubric.criteria.length) return `scores length ${scores.length} does not match rubric criteria length ${rubric.criteria.length}`;
|
|
4665
|
+
let composite = 0;
|
|
4666
|
+
for (let i = 0; i < rubric.criteria.length; i++) {
|
|
4667
|
+
const criterion = rubric.criteria[i];
|
|
4668
|
+
const score = scores[i];
|
|
4669
|
+
if (score.criterionId !== criterion.id) return `scores[${i}] has criterionId "${score.criterionId}" but rubric expects "${criterion.id}" in that position`;
|
|
4670
|
+
composite += criterion.weight * score.score;
|
|
4671
|
+
}
|
|
4672
|
+
const claimed = output.composite;
|
|
4673
|
+
if (Math.abs(claimed - composite) > 1e-6) return `composite ${claimed} does not match weighted sum ${composite.toFixed(6)}`;
|
|
4674
|
+
return null;
|
|
4675
|
+
}
|
|
4676
|
+
//#endregion
|
|
4612
4677
|
//#region ../../libs/tasks/src/task-types/render-pack.ts
|
|
4613
4678
|
/**
|
|
4614
4679
|
* `render_pack` — turn a context pack into a signed rendered artefact.
|
|
@@ -4776,6 +4841,18 @@ var BUILT_IN_TASK_TYPES = {
|
|
|
4776
4841
|
validateInput: validateJudgmentInput,
|
|
4777
4842
|
validateInputAsync: validateAssessBriefInputAsync
|
|
4778
4843
|
},
|
|
4844
|
+
[PR_REVIEW_TYPE]: {
|
|
4845
|
+
name: PR_REVIEW_TYPE,
|
|
4846
|
+
inputSchema: PrReviewInput,
|
|
4847
|
+
outputSchema: PrReviewOutput,
|
|
4848
|
+
outputKind: "judgment",
|
|
4849
|
+
workspaceMode: "dedicated_worktree",
|
|
4850
|
+
workspaceScope: "attempt",
|
|
4851
|
+
sessionScope: "none",
|
|
4852
|
+
requiresReferences: false,
|
|
4853
|
+
validateInput: validatePrReviewInput,
|
|
4854
|
+
validateOutput: validatePrReviewOutput
|
|
4855
|
+
},
|
|
4779
4856
|
[CURATE_PACK_TYPE]: {
|
|
4780
4857
|
name: CURATE_PACK_TYPE,
|
|
4781
4858
|
inputSchema: CuratePackInput,
|
|
@@ -6169,78 +6246,6 @@ function isHelpFlag(args) {
|
|
|
6169
6246
|
return args.includes("--help") || args.includes("-h");
|
|
6170
6247
|
}
|
|
6171
6248
|
//#endregion
|
|
6172
|
-
//#region ../../libs/agent-runtime/src/subagent-output-contracts.ts
|
|
6173
|
-
var REGISTRY = /* @__PURE__ */ new Map();
|
|
6174
|
-
/**
|
|
6175
|
-
* Register a subagent output contract. Idempotent: re-registering the
|
|
6176
|
-
* same name with a different schema throws — contracts are meant to
|
|
6177
|
-
* be stable. Re-registering with the identical contract object (same
|
|
6178
|
-
* reference) is a no-op for HMR and test convenience.
|
|
6179
|
-
*
|
|
6180
|
-
* Typically called at module-init time alongside task-type
|
|
6181
|
-
* registration. See task-types/index.ts in @moltnet/tasks for the
|
|
6182
|
-
* conventional pattern.
|
|
6183
|
-
*/
|
|
6184
|
-
function registerSubagentOutputContract(contract) {
|
|
6185
|
-
if (!contract.name || contract.name.trim().length === 0) throw new Error("subagent output contract name is required");
|
|
6186
|
-
if (!/^[a-z][a-z0-9_]*$/.test(contract.name)) throw new Error(`subagent output contract name '${contract.name}' must be lower_snake_case (starts with a letter, then [a-z0-9_]+)`);
|
|
6187
|
-
const existing = REGISTRY.get(contract.name);
|
|
6188
|
-
if (existing && existing !== contract) {
|
|
6189
|
-
if (existing.parametersSchema !== contract.parametersSchema) throw new Error(`subagent output contract '${contract.name}' is already registered with a different schema; refusing to override`);
|
|
6190
|
-
}
|
|
6191
|
-
REGISTRY.set(contract.name, contract);
|
|
6192
|
-
}
|
|
6193
|
-
/**
|
|
6194
|
-
* Resolve a subagent output contract by name. Returns `null` for
|
|
6195
|
-
* unknown names — callers (the subagent custom tool) decide whether
|
|
6196
|
-
* that's a tool error the parent LLM can recover from or a hard fail.
|
|
6197
|
-
*/
|
|
6198
|
-
function getSubagentOutputContract(name) {
|
|
6199
|
-
return REGISTRY.get(name) ?? null;
|
|
6200
|
-
}
|
|
6201
|
-
/**
|
|
6202
|
-
* List all registered contracts. Useful for diagnostics and for the
|
|
6203
|
-
* subagent tool's parameter description so a parent LLM can see what
|
|
6204
|
-
* contracts are available without enumerating them in its prompt.
|
|
6205
|
-
*/
|
|
6206
|
-
function listSubagentOutputContracts() {
|
|
6207
|
-
return [...REGISTRY.values()];
|
|
6208
|
-
}
|
|
6209
|
-
//#endregion
|
|
6210
|
-
//#region ../../libs/agent-runtime/src/built-in-contract-registrations.ts
|
|
6211
|
-
/**
|
|
6212
|
-
* Built-in subagent output contracts (#1087, #943).
|
|
6213
|
-
*
|
|
6214
|
-
* Why this is an exported function and not a module-init side
|
|
6215
|
-
* effect:
|
|
6216
|
-
*
|
|
6217
|
-
* - The registry is process-global. Module-init registration
|
|
6218
|
-
* fires exactly once per Node process (ESM modules are cached
|
|
6219
|
-
* by URL). Tests that call `__resetSubagentOutputContractsForTests()`
|
|
6220
|
-
* to start from an empty registry have no way to repopulate
|
|
6221
|
-
* the built-ins without re-evaluating the module — which the
|
|
6222
|
-
* cache prevents. PR #1101 review M4.
|
|
6223
|
-
* - An explicit `registerBuiltInSubagentContracts()` lets the
|
|
6224
|
-
* package index call it once at module load AND lets test
|
|
6225
|
-
* setup hooks call it again after `__reset...`.
|
|
6226
|
-
* - `registerSubagentOutputContract` is itself idempotent for
|
|
6227
|
-
* identical re-registrations, so calling this function twice
|
|
6228
|
-
* in the same process is safe.
|
|
6229
|
-
*
|
|
6230
|
-
* Adding a new built-in: extend the body of this function. Do not
|
|
6231
|
-
* call `registerSubagentOutputContract` from anywhere else in the
|
|
6232
|
-
* package — keeping all built-ins in one function makes the set
|
|
6233
|
-
* auditable.
|
|
6234
|
-
*/
|
|
6235
|
-
function registerBuiltInSubagentContracts() {
|
|
6236
|
-
registerSubagentOutputContract({
|
|
6237
|
-
name: "judge_eval_variant_result",
|
|
6238
|
-
description: "Per-variant grading result produced by a subagent of judge_eval_variant: scores against the shared rubric, composite, and a 1-3 sentence verdict for a single variant.",
|
|
6239
|
-
parametersSchema: JudgeEvalVariantResult
|
|
6240
|
-
});
|
|
6241
|
-
}
|
|
6242
|
-
registerBuiltInSubagentContracts();
|
|
6243
|
-
//#endregion
|
|
6244
6249
|
//#region ../../libs/agent-runtime/src/context-bindings.ts
|
|
6245
6250
|
var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
6246
6251
|
/**
|
|
@@ -6388,6 +6393,20 @@ function buildFinalOutputBlock(opts) {
|
|
|
6388
6393
|
return lines.join("\n");
|
|
6389
6394
|
}
|
|
6390
6395
|
//#endregion
|
|
6396
|
+
//#region ../../libs/agent-runtime/src/prompts/rubric-common.ts
|
|
6397
|
+
function renderRubricCriteriaList(rubric) {
|
|
6398
|
+
return rubric.criteria.map((c, i) => `${i + 1}. **${c.id}** (weight ${c.weight}, scoring: \`${c.scoring}\`) — ${c.description}`).join("\n");
|
|
6399
|
+
}
|
|
6400
|
+
function renderRubricPreambleSection(rubric) {
|
|
6401
|
+
if (!rubric.preamble) return null;
|
|
6402
|
+
return [
|
|
6403
|
+
"### Rubric preamble",
|
|
6404
|
+
"",
|
|
6405
|
+
rubric.preamble,
|
|
6406
|
+
""
|
|
6407
|
+
].join("\n");
|
|
6408
|
+
}
|
|
6409
|
+
//#endregion
|
|
6391
6410
|
//#region ../../libs/agent-runtime/src/prompts/assess-brief.ts
|
|
6392
6411
|
/**
|
|
6393
6412
|
* Build the first user-message prompt for an `assess_brief` judge attempt.
|
|
@@ -6413,13 +6432,8 @@ function buildFinalOutputBlock(opts) {
|
|
|
6413
6432
|
*/
|
|
6414
6433
|
function buildAssessBriefUserPrompt(input, ctx) {
|
|
6415
6434
|
const rubric = input.successCriteria.rubric;
|
|
6416
|
-
const criteriaList = rubric
|
|
6417
|
-
const preambleSection = rubric
|
|
6418
|
-
"### Rubric preamble",
|
|
6419
|
-
"",
|
|
6420
|
-
rubric.preamble,
|
|
6421
|
-
""
|
|
6422
|
-
].join("\n") : "";
|
|
6435
|
+
const criteriaList = renderRubricCriteriaList(rubric);
|
|
6436
|
+
const preambleSection = renderRubricPreambleSection(rubric) ?? "";
|
|
6423
6437
|
const workspaceSection = ctx.workspace?.mode === "dedicated_worktree" ? [
|
|
6424
6438
|
"### Workspace",
|
|
6425
6439
|
"",
|
|
@@ -6700,13 +6714,7 @@ function buildCuratePackUserPrompt(input, ctx) {
|
|
|
6700
6714
|
* is told to inspect them itself.
|
|
6701
6715
|
*/
|
|
6702
6716
|
function buildFulfillBriefUserPrompt(input, ctx) {
|
|
6703
|
-
const { brief, title,
|
|
6704
|
-
const criteriaSection = acceptanceCriteria?.length ? [
|
|
6705
|
-
"### Acceptance criteria",
|
|
6706
|
-
"",
|
|
6707
|
-
...acceptanceCriteria.map((c) => `- ${c}`),
|
|
6708
|
-
""
|
|
6709
|
-
].join("\n") : "";
|
|
6717
|
+
const { brief, title, seedFiles, scopeHint } = input;
|
|
6710
6718
|
const seedSection = seedFiles?.length ? [
|
|
6711
6719
|
"### Seed files",
|
|
6712
6720
|
"",
|
|
@@ -6754,7 +6762,6 @@ function buildFulfillBriefUserPrompt(input, ctx) {
|
|
|
6754
6762
|
"",
|
|
6755
6763
|
brief,
|
|
6756
6764
|
"",
|
|
6757
|
-
criteriaSection,
|
|
6758
6765
|
seedSection,
|
|
6759
6766
|
correlationSection,
|
|
6760
6767
|
workspaceSection,
|
|
@@ -6894,13 +6901,8 @@ function buildJudgeEvalVariantUserPrompt(input, ctx) {
|
|
|
6894
6901
|
function buildJudgePackUserPrompt(input, ctx) {
|
|
6895
6902
|
const { renderedPackId, sourcePackId, successCriteria } = input;
|
|
6896
6903
|
const rubric = successCriteria.rubric;
|
|
6897
|
-
const criteriaList = rubric
|
|
6898
|
-
const preambleSection = rubric
|
|
6899
|
-
"### Rubric preamble",
|
|
6900
|
-
"",
|
|
6901
|
-
rubric.preamble,
|
|
6902
|
-
""
|
|
6903
|
-
].join("\n") : null;
|
|
6904
|
+
const criteriaList = renderRubricCriteriaList(rubric);
|
|
6905
|
+
const preambleSection = renderRubricPreambleSection(rubric);
|
|
6904
6906
|
return [
|
|
6905
6907
|
"# Judge Pack Agent",
|
|
6906
6908
|
"",
|
|
@@ -7016,6 +7018,112 @@ function buildJudgePackUserPrompt(input, ctx) {
|
|
|
7016
7018
|
].filter((l) => l !== null).join("\n");
|
|
7017
7019
|
}
|
|
7018
7020
|
//#endregion
|
|
7021
|
+
//#region ../../libs/agent-runtime/src/prompts/pr-review.ts
|
|
7022
|
+
function buildPrReviewUserPrompt(input, ctx) {
|
|
7023
|
+
const rubric = input.successCriteria.rubric;
|
|
7024
|
+
const criteriaList = renderRubricCriteriaList(rubric);
|
|
7025
|
+
const preambleSection = renderRubricPreambleSection(rubric);
|
|
7026
|
+
const taskPromptSection = input.taskPrompt ? [
|
|
7027
|
+
"## Task-specific instructions",
|
|
7028
|
+
"",
|
|
7029
|
+
input.taskPrompt,
|
|
7030
|
+
""
|
|
7031
|
+
].join("\n") : "";
|
|
7032
|
+
const resourceSection = input.subject.resourceUrls && input.subject.resourceUrls.length > 0 ? [
|
|
7033
|
+
"### Resources",
|
|
7034
|
+
"",
|
|
7035
|
+
...input.subject.resourceUrls.map((url) => `- ${url}`),
|
|
7036
|
+
""
|
|
7037
|
+
].join("\n") : "";
|
|
7038
|
+
const hintsSection = input.subject.inspectionHints && input.subject.inspectionHints.length > 0 ? [
|
|
7039
|
+
"### Inspection hints",
|
|
7040
|
+
"",
|
|
7041
|
+
...input.subject.inspectionHints.map((hint) => `- ${hint}`),
|
|
7042
|
+
""
|
|
7043
|
+
].join("\n") : "";
|
|
7044
|
+
const workspaceSection = ctx.workspace?.mode === "dedicated_worktree" ? [
|
|
7045
|
+
"### Workspace",
|
|
7046
|
+
"",
|
|
7047
|
+
"This review attempt is running inside a dedicated disposable git",
|
|
7048
|
+
"worktree. Inspect and reason inside this workspace only.",
|
|
7049
|
+
ctx.workspace.branch ? `The current review branch is \`${ctx.workspace.branch}\`.` : "The current checkout is disposable and will be cleaned up when the task ends.",
|
|
7050
|
+
""
|
|
7051
|
+
].join("\n") : "";
|
|
7052
|
+
return [
|
|
7053
|
+
"# Review Agent",
|
|
7054
|
+
"",
|
|
7055
|
+
"You are an independent judge. You did NOT produce the subject under review.",
|
|
7056
|
+
"Assess it strictly against the rubric below and emit a structured judgment.",
|
|
7057
|
+
"You may inspect the local workspace and the referenced resources, but do NOT modify anything.",
|
|
7058
|
+
"",
|
|
7059
|
+
`Your diary ID is: ${ctx.diaryId}`,
|
|
7060
|
+
`This task's id is: ${ctx.taskId}`,
|
|
7061
|
+
"",
|
|
7062
|
+
"## Subject",
|
|
7063
|
+
"",
|
|
7064
|
+
`**Title:** ${input.subject.title}`,
|
|
7065
|
+
"",
|
|
7066
|
+
input.subject.summary,
|
|
7067
|
+
"",
|
|
7068
|
+
resourceSection,
|
|
7069
|
+
hintsSection,
|
|
7070
|
+
workspaceSection,
|
|
7071
|
+
"### Execution contract",
|
|
7072
|
+
"",
|
|
7073
|
+
"Treat the provided subject, resources, inspection hints, and any",
|
|
7074
|
+
"task-specific instructions as the full",
|
|
7075
|
+
"review contract for this task.",
|
|
7076
|
+
"",
|
|
7077
|
+
"If the task-specific instructions or inspection hints require an outward action tied to the review",
|
|
7078
|
+
"(for example publishing the judgment somewhere), perform that action as",
|
|
7079
|
+
"part of the task before reporting structured output.",
|
|
7080
|
+
"",
|
|
7081
|
+
"## Review workflow",
|
|
7082
|
+
"",
|
|
7083
|
+
"1. Read the subject summary, resources, inspection hints, and any",
|
|
7084
|
+
" task-specific instructions before scoring.",
|
|
7085
|
+
"2. Inspect the target artefact directly using the tools and resources the",
|
|
7086
|
+
" task makes available.",
|
|
7087
|
+
"3. If you are in a dedicated disposable worktree and need the review target",
|
|
7088
|
+
" checked out locally, do that work inside this disposable workspace only.",
|
|
7089
|
+
"4. Apply the rubric strictly. This task is about complexity and",
|
|
7090
|
+
" reviewability, not correctness or feature desirability.",
|
|
7091
|
+
"5. Perform any required outward action before emitting the final",
|
|
7092
|
+
" structured output.",
|
|
7093
|
+
"",
|
|
7094
|
+
taskPromptSection,
|
|
7095
|
+
preambleSection,
|
|
7096
|
+
"## Criteria",
|
|
7097
|
+
"",
|
|
7098
|
+
criteriaList,
|
|
7099
|
+
"",
|
|
7100
|
+
"### Scoring rules",
|
|
7101
|
+
"",
|
|
7102
|
+
"- Every criterion uses binary scoring only.",
|
|
7103
|
+
"- Score `1` when the subject clearly clears the criterion.",
|
|
7104
|
+
"- Score `0` when it does not, or when the evidence is ambiguous.",
|
|
7105
|
+
"- `rationale` is REQUIRED for every score. Keep it concrete and audit-friendly.",
|
|
7106
|
+
"- Compute `composite = Σ(weight_i × score_i)` exactly; the runtime rejects mismatches.",
|
|
7107
|
+
"",
|
|
7108
|
+
"Write a signed diary entry (tags: `judgment`, `pr_review`) capturing the rationale before reporting structured output.",
|
|
7109
|
+
"",
|
|
7110
|
+
buildFinalOutputBlock({
|
|
7111
|
+
taskType: "pr_review",
|
|
7112
|
+
outputSchemaName: "PrReviewOutput",
|
|
7113
|
+
shapeSketch: [
|
|
7114
|
+
"{",
|
|
7115
|
+
" \"scores\": [",
|
|
7116
|
+
" { \"criterionId\": \"...\", \"score\": 0, \"rationale\": \"...\" }",
|
|
7117
|
+
" ],",
|
|
7118
|
+
" \"composite\": <sum-of-weighted-binary-scores>,",
|
|
7119
|
+
" \"verdict\": \"<1-3 sentence overall>\"",
|
|
7120
|
+
"}"
|
|
7121
|
+
].join("\n"),
|
|
7122
|
+
extraNotes: ["`scores` MUST stay in the same order as the rubric criteria.", "`score` MUST be exactly `0` or `1` for every criterion."]
|
|
7123
|
+
})
|
|
7124
|
+
].filter(Boolean).join("\n");
|
|
7125
|
+
}
|
|
7126
|
+
//#endregion
|
|
7019
7127
|
//#region ../../libs/agent-runtime/src/prompts/render-pack.ts
|
|
7020
7128
|
/**
|
|
7021
7129
|
* Build the first user-message prompt for a `render_pack` task. Almost mechanical:
|
|
@@ -7198,6 +7306,16 @@ function buildTaskUserPrompt(task, ctx) {
|
|
|
7198
7306
|
diaryId: ctx.diaryId,
|
|
7199
7307
|
taskId: ctx.taskId
|
|
7200
7308
|
});
|
|
7309
|
+
case PR_REVIEW_TYPE:
|
|
7310
|
+
if (!Check(PrReviewInput, task.input)) {
|
|
7311
|
+
const errors = [...Errors(PrReviewInput, task.input)];
|
|
7312
|
+
throw new Error(`pr_review input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
7313
|
+
}
|
|
7314
|
+
return buildPrReviewUserPrompt(task.input, {
|
|
7315
|
+
diaryId: ctx.diaryId,
|
|
7316
|
+
taskId: ctx.taskId,
|
|
7317
|
+
workspace: ctx.workspace
|
|
7318
|
+
});
|
|
7201
7319
|
case JUDGE_EVAL_VARIANT_TYPE:
|
|
7202
7320
|
if (!Check(JudgeEvalVariantInput, task.input)) {
|
|
7203
7321
|
const errors = [...Errors(JudgeEvalVariantInput, task.input)];
|
|
@@ -13916,6 +14034,31 @@ function abortableSleep(ms, signal) {
|
|
|
13916
14034
|
});
|
|
13917
14035
|
}
|
|
13918
14036
|
//#endregion
|
|
14037
|
+
//#region ../../libs/agent-runtime/src/subagent-output-contracts.ts
|
|
14038
|
+
/**
|
|
14039
|
+
* Construct an immutable contract registry from a static list.
|
|
14040
|
+
*
|
|
14041
|
+
* The resulting registry is safe to share across sessions and
|
|
14042
|
+
* invocations — no mutation is possible after construction.
|
|
14043
|
+
*/
|
|
14044
|
+
function createSubagentContractRegistry(contracts) {
|
|
14045
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
14046
|
+
for (const c of contracts) {
|
|
14047
|
+
if (!c.name || c.name.trim().length === 0) throw new Error("subagent output contract name is required");
|
|
14048
|
+
if (!/^[a-z][a-z0-9_]*$/.test(c.name)) throw new Error(`subagent output contract name '${c.name}' must be lower_snake_case (starts with a letter, then [a-z0-9_]+)`);
|
|
14049
|
+
if (lookup.has(c.name)) throw new Error(`duplicate subagent output contract name '${c.name}' in constructor args`);
|
|
14050
|
+
lookup.set(c.name, c);
|
|
14051
|
+
}
|
|
14052
|
+
return {
|
|
14053
|
+
get(name) {
|
|
14054
|
+
return lookup.get(name) ?? null;
|
|
14055
|
+
},
|
|
14056
|
+
list() {
|
|
14057
|
+
return [...lookup.values()];
|
|
14058
|
+
}
|
|
14059
|
+
};
|
|
14060
|
+
}
|
|
14061
|
+
//#endregion
|
|
13919
14062
|
//#region ../../libs/pi-extension/src/moltnet/render-phase6.ts
|
|
13920
14063
|
function slugToTitle(value) {
|
|
13921
14064
|
return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -14892,7 +15035,8 @@ function findMainWorktree() {
|
|
|
14892
15035
|
function loadCredentials(agentDir) {
|
|
14893
15036
|
const moltnetJson = readFileSync(path.join(agentDir, "moltnet.json"), "utf8");
|
|
14894
15037
|
const agentEnvRaw = readFileSync(path.join(agentDir, "env"), "utf8");
|
|
14895
|
-
const
|
|
15038
|
+
const piAgentDir = process.env.PI_CODING_AGENT_DIR ?? path.join(process.env.HOME ?? "", ".pi", "agent");
|
|
15039
|
+
const piAuthPath = path.join(piAgentDir, "auth.json");
|
|
14896
15040
|
const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
|
|
14897
15041
|
const gitconfigPath = path.join(agentDir, "gitconfig");
|
|
14898
15042
|
const gitconfig = existsSync(gitconfigPath) ? readFileSync(gitconfigPath, "utf8") : null;
|
|
@@ -15404,7 +15548,7 @@ async function buildAgentSession(args) {
|
|
|
15404
15548
|
spanAttributes: args.otelSpanAttrs
|
|
15405
15549
|
});
|
|
15406
15550
|
const resourceLoader = new DefaultResourceLoader({
|
|
15407
|
-
cwd: args.
|
|
15551
|
+
cwd: args.cwdPath,
|
|
15408
15552
|
agentDir: args.piAuthDir,
|
|
15409
15553
|
extensionFactories: [piOtelExtension],
|
|
15410
15554
|
appendSystemPrompt: args.appendSystemPrompt,
|
|
@@ -15412,12 +15556,12 @@ async function buildAgentSession(args) {
|
|
|
15412
15556
|
});
|
|
15413
15557
|
await resourceLoader.reload();
|
|
15414
15558
|
const sessionManager = args.sessionPersistence ? await resolvePersistentSessionManager({
|
|
15415
|
-
cwd: args.
|
|
15559
|
+
cwd: args.cwdPath,
|
|
15416
15560
|
sessionDir: args.sessionPersistence.sessionDir
|
|
15417
|
-
}) : SessionManager.inMemory(args.
|
|
15561
|
+
}) : SessionManager.inMemory(args.cwdPath);
|
|
15418
15562
|
return (await createAgentSession({
|
|
15419
15563
|
agentDir: args.piAuthDir,
|
|
15420
|
-
cwd: args.
|
|
15564
|
+
cwd: args.cwdPath,
|
|
15421
15565
|
model: args.modelHandle,
|
|
15422
15566
|
customTools: args.customTools,
|
|
15423
15567
|
sessionManager,
|
|
@@ -15662,6 +15806,7 @@ var DEFAULT_SUBAGENT_TIMEOUT_MS = 300 * 1e3;
|
|
|
15662
15806
|
*/
|
|
15663
15807
|
function createSubagentTool(args) {
|
|
15664
15808
|
const buildSession = args.buildAgentSession ?? buildAgentSession;
|
|
15809
|
+
const { contractRegistry } = args;
|
|
15665
15810
|
let callCount = 0;
|
|
15666
15811
|
return {
|
|
15667
15812
|
tool: defineTool({
|
|
@@ -15672,8 +15817,8 @@ function createSubagentTool(args) {
|
|
|
15672
15817
|
async execute(_id, params) {
|
|
15673
15818
|
if (!Check(SubagentToolParameters, params)) return toolError(`subagent: invalid parameters: ${JSON.stringify([...Errors(SubagentToolParameters, params)].slice(0, 3))}`);
|
|
15674
15819
|
const { task, output_schema } = params;
|
|
15675
|
-
const contract =
|
|
15676
|
-
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${
|
|
15820
|
+
const contract = contractRegistry.get(output_schema);
|
|
15821
|
+
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${contractRegistry.list().map((c) => c.name).join(", ")}]`);
|
|
15677
15822
|
callCount += 1;
|
|
15678
15823
|
const callIndex = callCount;
|
|
15679
15824
|
let captured = null;
|
|
@@ -15703,6 +15848,7 @@ function createSubagentTool(args) {
|
|
|
15703
15848
|
});
|
|
15704
15849
|
const session = await buildSession({
|
|
15705
15850
|
mountPath: args.mountPath,
|
|
15851
|
+
cwdPath: args.cwdPath ?? args.mountPath,
|
|
15706
15852
|
piAuthDir: args.piAuthDir,
|
|
15707
15853
|
modelHandle: args.modelHandle,
|
|
15708
15854
|
agentName: args.agentName,
|
|
@@ -16043,6 +16189,7 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16043
16189
|
const branch = executionPlan?.worktreeBranch ?? null;
|
|
16044
16190
|
if (!branch) return {
|
|
16045
16191
|
mountPath: requestedMountPath,
|
|
16192
|
+
cwdPath: requestedMountPath,
|
|
16046
16193
|
mode: "shared_mount",
|
|
16047
16194
|
branch: null,
|
|
16048
16195
|
cleanup: () => {}
|
|
@@ -16050,7 +16197,7 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16050
16197
|
const mainRepo = findMainWorktree();
|
|
16051
16198
|
const worktreeDir = resolveTaskWorktreePath(mainRepo, executionPlan?.workspaceId ?? `task-${task.id}`);
|
|
16052
16199
|
const relMount = relative(mainRepo, requestedMountPath);
|
|
16053
|
-
const
|
|
16200
|
+
const cwdPath = relMount === "" || relMount.startsWith("..") ? worktreeDir : join(worktreeDir, relMount);
|
|
16054
16201
|
const keepWorkspace = executionPlan?.workspaceScope === "session" && executionPlan.sessionKey !== null;
|
|
16055
16202
|
if (keepWorkspace) ensureReusableTaskWorktree(mainRepo, worktreeDir, branch);
|
|
16056
16203
|
else {
|
|
@@ -16058,7 +16205,8 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16058
16205
|
addTaskWorktree(mainRepo, worktreeDir, branch);
|
|
16059
16206
|
}
|
|
16060
16207
|
return {
|
|
16061
|
-
mountPath,
|
|
16208
|
+
mountPath: mainRepo,
|
|
16209
|
+
cwdPath,
|
|
16062
16210
|
mode: "dedicated_worktree",
|
|
16063
16211
|
branch,
|
|
16064
16212
|
cleanup: keepWorkspace ? () => {} : () => {
|
|
@@ -16171,15 +16319,24 @@ var noopTurnEventHandler = () => {};
|
|
|
16171
16319
|
function createPiTaskExecutor(opts) {
|
|
16172
16320
|
let cachedCheckpoint = opts.checkpointPath ?? null;
|
|
16173
16321
|
return async (claimedTask, reporter) => {
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
})
|
|
16322
|
+
const reporterWasOpened = !reporter.cancelSignal.aborted;
|
|
16323
|
+
if (reporterWasOpened) await reporter.open({
|
|
16324
|
+
taskId: claimedTask.task.id,
|
|
16325
|
+
attemptN: claimedTask.attemptN
|
|
16179
16326
|
});
|
|
16180
16327
|
return executePiTask(claimedTask, reporter, {
|
|
16181
16328
|
...opts,
|
|
16182
|
-
checkpointPath: cachedCheckpoint
|
|
16329
|
+
checkpointPath: cachedCheckpoint ?? void 0,
|
|
16330
|
+
resolveCheckpointPath: async () => {
|
|
16331
|
+
if (!cachedCheckpoint) cachedCheckpoint = await ensureSnapshot({
|
|
16332
|
+
config: opts.sandboxConfig?.snapshot,
|
|
16333
|
+
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
16334
|
+
process.stderr.write(`[snapshot] ${m}\n`);
|
|
16335
|
+
})
|
|
16336
|
+
});
|
|
16337
|
+
return cachedCheckpoint;
|
|
16338
|
+
},
|
|
16339
|
+
reporterAlreadyOpened: reporterWasOpened
|
|
16183
16340
|
});
|
|
16184
16341
|
};
|
|
16185
16342
|
}
|
|
@@ -16195,8 +16352,9 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16195
16352
|
const startTime = Date.now();
|
|
16196
16353
|
const requestedMountPath = opts.mountPath ?? process.cwd();
|
|
16197
16354
|
const executionPlan = opts.makeExecutionPlan?.(claimedTask) ?? null;
|
|
16198
|
-
|
|
16199
|
-
|
|
16355
|
+
let workspace = null;
|
|
16356
|
+
let mountPath = requestedMountPath;
|
|
16357
|
+
let cwdPath = requestedMountPath;
|
|
16200
16358
|
if (reporter.cancelSignal.aborted) return {
|
|
16201
16359
|
taskId: task.id,
|
|
16202
16360
|
attemptN,
|
|
@@ -16211,33 +16369,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16211
16369
|
retryable: false
|
|
16212
16370
|
}
|
|
16213
16371
|
};
|
|
16214
|
-
|
|
16215
|
-
config: opts.sandboxConfig?.snapshot,
|
|
16216
|
-
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
16217
|
-
process.stderr.write(`[snapshot] ${m}\n`);
|
|
16218
|
-
})
|
|
16219
|
-
});
|
|
16220
|
-
const mainRepoForRepair = findMainWorktree();
|
|
16221
|
-
try {
|
|
16222
|
-
execFileSync("git", [
|
|
16223
|
-
"-C",
|
|
16224
|
-
mainRepoForRepair,
|
|
16225
|
-
"worktree",
|
|
16226
|
-
"repair",
|
|
16227
|
-
"--relative-paths"
|
|
16228
|
-
], { stdio: "pipe" });
|
|
16229
|
-
} catch {}
|
|
16372
|
+
let reporterOpen = opts.reporterAlreadyOpened ?? false;
|
|
16230
16373
|
let managed = null;
|
|
16231
|
-
managed = await resumeVm({
|
|
16232
|
-
checkpointPath,
|
|
16233
|
-
agentName: opts.agentName,
|
|
16234
|
-
mountPath,
|
|
16235
|
-
extraAllowedHosts: opts.extraAllowedHosts,
|
|
16236
|
-
sandboxConfig: opts.sandboxConfig
|
|
16237
|
-
});
|
|
16238
|
-
const diaryId = task.diaryId ?? "";
|
|
16239
|
-
const taskTeamId = task.teamId ?? "";
|
|
16240
|
-
let reporterOpen = false;
|
|
16241
16374
|
let session = null;
|
|
16242
16375
|
let subagentHandle = null;
|
|
16243
16376
|
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
@@ -16256,41 +16389,103 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16256
16389
|
retryable: false
|
|
16257
16390
|
}
|
|
16258
16391
|
});
|
|
16392
|
+
let onTurnEvent;
|
|
16393
|
+
if (opts.makeOnTurnEvent) try {
|
|
16394
|
+
onTurnEvent = opts.makeOnTurnEvent(claimedTask);
|
|
16395
|
+
} catch (err) {
|
|
16396
|
+
process.stderr.write(`[emit] makeOnTurnEvent threw: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
16397
|
+
onTurnEvent = noopTurnEventHandler;
|
|
16398
|
+
}
|
|
16399
|
+
else onTurnEvent = opts.onTurnEvent ?? noopTurnEventHandler;
|
|
16400
|
+
const emit = (kind, payload) => {
|
|
16401
|
+
try {
|
|
16402
|
+
onTurnEvent(kind, summarizePayloadForLog(kind, payload));
|
|
16403
|
+
} catch (err) {
|
|
16404
|
+
process.stderr.write(`[emit] onTurnEvent threw for kind="${kind}": ${err instanceof Error ? err.message : String(err)}\n`);
|
|
16405
|
+
}
|
|
16406
|
+
return reporter.record({
|
|
16407
|
+
kind,
|
|
16408
|
+
payload
|
|
16409
|
+
});
|
|
16410
|
+
};
|
|
16411
|
+
const emitError = async (phase, message, extra = {}) => {
|
|
16412
|
+
await emit("error", {
|
|
16413
|
+
phase,
|
|
16414
|
+
message,
|
|
16415
|
+
...extra
|
|
16416
|
+
});
|
|
16417
|
+
};
|
|
16259
16418
|
try {
|
|
16260
|
-
|
|
16261
|
-
activateAgentEnv(managed.credentials.agentEnv, mainRepo);
|
|
16262
|
-
await reporter.open({
|
|
16419
|
+
if (!opts.reporterAlreadyOpened) await reporter.open({
|
|
16263
16420
|
taskId: task.id,
|
|
16264
16421
|
attemptN
|
|
16265
16422
|
});
|
|
16266
16423
|
reporterOpen = true;
|
|
16267
|
-
let
|
|
16268
|
-
|
|
16269
|
-
|
|
16424
|
+
let checkpointPath;
|
|
16425
|
+
try {
|
|
16426
|
+
checkpointPath = opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
|
|
16427
|
+
config: opts.sandboxConfig?.snapshot,
|
|
16428
|
+
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
16429
|
+
process.stderr.write(`[snapshot] ${m}\n`);
|
|
16430
|
+
})
|
|
16431
|
+
}));
|
|
16432
|
+
} catch (err) {
|
|
16433
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16434
|
+
await emitError("snapshot", message);
|
|
16435
|
+
return makeFailedOutput("snapshot_failed", message);
|
|
16436
|
+
}
|
|
16437
|
+
try {
|
|
16438
|
+
workspace = prepareTaskWorkspace(task, requestedMountPath, executionPlan);
|
|
16439
|
+
mountPath = workspace.mountPath;
|
|
16440
|
+
cwdPath = workspace.cwdPath;
|
|
16270
16441
|
} catch (err) {
|
|
16271
|
-
|
|
16272
|
-
|
|
16442
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16443
|
+
await emitError("worktree_setup", message);
|
|
16444
|
+
return makeFailedOutput("worktree_setup_failed", message);
|
|
16273
16445
|
}
|
|
16274
|
-
|
|
16275
|
-
|
|
16446
|
+
try {
|
|
16447
|
+
const mainRepoForRepair = findMainWorktree();
|
|
16276
16448
|
try {
|
|
16277
|
-
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16449
|
+
execFileSync("git", [
|
|
16450
|
+
"-C",
|
|
16451
|
+
mainRepoForRepair,
|
|
16452
|
+
"worktree",
|
|
16453
|
+
"repair",
|
|
16454
|
+
"--relative-paths"
|
|
16455
|
+
], { stdio: "pipe" });
|
|
16456
|
+
} catch {}
|
|
16457
|
+
} catch (err) {
|
|
16458
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16459
|
+
await emitError("worktree_setup", message);
|
|
16460
|
+
return makeFailedOutput("worktree_setup_failed", message);
|
|
16461
|
+
}
|
|
16462
|
+
try {
|
|
16463
|
+
managed = await resumeVm({
|
|
16464
|
+
checkpointPath,
|
|
16465
|
+
agentName: opts.agentName,
|
|
16466
|
+
mountPath,
|
|
16467
|
+
extraAllowedHosts: opts.extraAllowedHosts,
|
|
16468
|
+
sandboxConfig: opts.sandboxConfig
|
|
16284
16469
|
});
|
|
16285
|
-
}
|
|
16470
|
+
} catch (err) {
|
|
16471
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16472
|
+
await emitError("vm_resume", message);
|
|
16473
|
+
return makeFailedOutput("vm_resume_failed", message);
|
|
16474
|
+
}
|
|
16475
|
+
const diaryId = task.diaryId ?? "";
|
|
16476
|
+
const taskTeamId = task.teamId ?? "";
|
|
16477
|
+
const mainRepo = findMainWorktree();
|
|
16478
|
+
activateAgentEnv(managed.credentials.agentEnv, mainRepo);
|
|
16479
|
+
const activeWorkspace = workspace;
|
|
16480
|
+
if (!activeWorkspace) throw new Error("task workspace not prepared");
|
|
16286
16481
|
await emit("info", {
|
|
16287
16482
|
event: "execute_start",
|
|
16288
16483
|
taskType: task.taskType,
|
|
16289
16484
|
teamId: task.teamId,
|
|
16290
16485
|
provider: opts.provider,
|
|
16291
16486
|
model: opts.model,
|
|
16292
|
-
workspaceMode:
|
|
16293
|
-
workspaceBranch:
|
|
16487
|
+
workspaceMode: activeWorkspace.mode,
|
|
16488
|
+
workspaceBranch: activeWorkspace.branch
|
|
16294
16489
|
});
|
|
16295
16490
|
let taskPrompt;
|
|
16296
16491
|
try {
|
|
@@ -16298,8 +16493,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16298
16493
|
diaryId,
|
|
16299
16494
|
taskId: task.id,
|
|
16300
16495
|
workspace: {
|
|
16301
|
-
mode:
|
|
16302
|
-
branch:
|
|
16496
|
+
mode: activeWorkspace.mode,
|
|
16497
|
+
branch: activeWorkspace.branch
|
|
16303
16498
|
},
|
|
16304
16499
|
extras: opts.promptExtras
|
|
16305
16500
|
});
|
|
@@ -16351,7 +16546,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16351
16546
|
getTeamId: () => taskTeamId,
|
|
16352
16547
|
getSessionErrors: () => [],
|
|
16353
16548
|
clearSessionErrors: () => {},
|
|
16354
|
-
getHostCwd: () =>
|
|
16549
|
+
getHostCwd: () => cwdPath,
|
|
16355
16550
|
hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
|
|
16356
16551
|
hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
|
|
16357
16552
|
getTaskContext: () => ({
|
|
@@ -16379,6 +16574,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16379
16574
|
if (taskTypeUsesSubagents(task.taskType)) {
|
|
16380
16575
|
subagentHandle = createSubagentTool({
|
|
16381
16576
|
mountPath,
|
|
16577
|
+
cwdPath,
|
|
16382
16578
|
piAuthDir,
|
|
16383
16579
|
modelHandle,
|
|
16384
16580
|
agentName: opts.agentName,
|
|
@@ -16387,12 +16583,14 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16387
16583
|
parentTaskId: task.id,
|
|
16388
16584
|
parentTaskType: task.taskType,
|
|
16389
16585
|
parentAttemptN: attemptN,
|
|
16586
|
+
contractRegistry: opts.subagentContractRegistry,
|
|
16390
16587
|
parentCancelSignal: reporter.cancelSignal
|
|
16391
16588
|
});
|
|
16392
16589
|
parentSubagentTools.push(subagentHandle.tool);
|
|
16393
16590
|
}
|
|
16394
16591
|
session = await buildAgentSession({
|
|
16395
16592
|
mountPath,
|
|
16593
|
+
cwdPath,
|
|
16396
16594
|
piAuthDir,
|
|
16397
16595
|
modelHandle,
|
|
16398
16596
|
agentName: opts.agentName,
|
|
@@ -16477,6 +16675,10 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16477
16675
|
is_error: event.isError,
|
|
16478
16676
|
result: event.isError ? truncateForWire(event.result) : void 0
|
|
16479
16677
|
}));
|
|
16678
|
+
if (event.isError) track(emitError("tool_call_error", describeToolErrorMessage(event.result), {
|
|
16679
|
+
tool: event.toolName,
|
|
16680
|
+
result: truncateForWire(event.result)
|
|
16681
|
+
}));
|
|
16480
16682
|
if (maxBashTimeouts > 0 && event.toolName === "bash" && event.isError && isBashTimeoutResult(event.result)) {
|
|
16481
16683
|
bashTimeoutCount += 1;
|
|
16482
16684
|
if (bashTimeoutCount >= maxBashTimeouts) triggerCapAbort("max_bash_timeouts_exceeded", `Aborted after ${bashTimeoutCount} bash timeouts in this attempt (cap ${maxBashTimeouts}).`);
|
|
@@ -16632,7 +16834,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16632
16834
|
}
|
|
16633
16835
|
}
|
|
16634
16836
|
if (managed) await managed.vm.close();
|
|
16635
|
-
try {
|
|
16837
|
+
if (workspace) try {
|
|
16636
16838
|
workspace.cleanup();
|
|
16637
16839
|
} catch (err) {
|
|
16638
16840
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -16743,6 +16945,23 @@ function truncateForWire(value) {
|
|
|
16743
16945
|
};
|
|
16744
16946
|
}
|
|
16745
16947
|
}
|
|
16948
|
+
function describeToolErrorMessage(result) {
|
|
16949
|
+
if (typeof result === "string" && result.trim().length > 0) return result.trim();
|
|
16950
|
+
if (result && typeof result === "object") {
|
|
16951
|
+
const content = result.content;
|
|
16952
|
+
if (Array.isArray(content)) {
|
|
16953
|
+
for (const item of content) if (item && typeof item === "object" && typeof item.text === "string") {
|
|
16954
|
+
const text = item.text.trim();
|
|
16955
|
+
if (text.length > 0) return text;
|
|
16956
|
+
}
|
|
16957
|
+
}
|
|
16958
|
+
}
|
|
16959
|
+
try {
|
|
16960
|
+
return JSON.stringify(truncateForWire(result));
|
|
16961
|
+
} catch {
|
|
16962
|
+
return "Tool call failed";
|
|
16963
|
+
}
|
|
16964
|
+
}
|
|
16746
16965
|
//#endregion
|
|
16747
16966
|
//#region src/config.ts
|
|
16748
16967
|
function loadConfig() {
|
|
@@ -17619,9 +17838,15 @@ async function runPolling(opts) {
|
|
|
17619
17838
|
maxPollIntervalMs
|
|
17620
17839
|
}, "agent-daemon.starting");
|
|
17621
17840
|
const outputs = [];
|
|
17841
|
+
const subagentContractRegistry = createSubagentContractRegistry([{
|
|
17842
|
+
name: "judge_eval_variant_result",
|
|
17843
|
+
description: "Per-variant grading result produced by a subagent of judge_eval_variant: scores against the shared rubric, composite, and a 1-3 sentence verdict for a single variant.",
|
|
17844
|
+
parametersSchema: JudgeEvalVariantResult
|
|
17845
|
+
}]);
|
|
17622
17846
|
try {
|
|
17623
17847
|
const executeTask = createPiTaskExecutor({
|
|
17624
17848
|
agentName: common.agent,
|
|
17849
|
+
subagentContractRegistry,
|
|
17625
17850
|
mountPath: sandbox.rootDir,
|
|
17626
17851
|
provider: common.provider,
|
|
17627
17852
|
model: common.model,
|
|
@@ -17667,11 +17892,19 @@ async function runPolling(opts) {
|
|
|
17667
17892
|
executeTask: async (claimedTask, reporter) => {
|
|
17668
17893
|
const executionPlan = executionPlans.getOrCreate(claimedTask);
|
|
17669
17894
|
const sessionDescriptor = executionPlan.descriptor;
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17895
|
+
let expired;
|
|
17896
|
+
try {
|
|
17897
|
+
expired = slotRegistry.reapExpiredSlots();
|
|
17898
|
+
if (expired.length > 0) rootLogger.info({
|
|
17899
|
+
expiredCount: expired.length,
|
|
17900
|
+
slotKeys: expired.map((item) => item.slot.slotKey)
|
|
17901
|
+
}, "agent-daemon.daemon_slots_reaped");
|
|
17902
|
+
} catch (err) {
|
|
17903
|
+
rootLogger.error({
|
|
17904
|
+
phase: "daemon_slot_reap",
|
|
17905
|
+
err: err instanceof Error ? err.message : String(err)
|
|
17906
|
+
}, "agent-daemon.daemon_slot_reap_failed");
|
|
17907
|
+
}
|
|
17675
17908
|
rootLogger.debug({
|
|
17676
17909
|
taskId: claimedTask.task.id,
|
|
17677
17910
|
taskType: claimedTask.task.taskType,
|
|
@@ -17866,9 +18099,15 @@ async function runOnce(argv) {
|
|
|
17866
18099
|
process.on("SIGTERM", () => {
|
|
17867
18100
|
onSignal("SIGTERM");
|
|
17868
18101
|
});
|
|
18102
|
+
const subagentContractRegistry = createSubagentContractRegistry([{
|
|
18103
|
+
name: "judge_eval_variant_result",
|
|
18104
|
+
description: "Per-variant grading result produced by a subagent of judge_eval_variant: scores against the shared rubric, composite, and a 1-3 sentence verdict for a single variant.",
|
|
18105
|
+
parametersSchema: JudgeEvalVariantResult
|
|
18106
|
+
}]);
|
|
17869
18107
|
try {
|
|
17870
18108
|
const rawExecuteTask = createPiTaskExecutor({
|
|
17871
18109
|
agentName: opts.agent,
|
|
18110
|
+
subagentContractRegistry,
|
|
17872
18111
|
mountPath: sandbox.rootDir,
|
|
17873
18112
|
provider: opts.provider,
|
|
17874
18113
|
model: opts.model,
|
|
@@ -17879,11 +18118,18 @@ async function runOnce(argv) {
|
|
|
17879
18118
|
maxBashTimeouts: opts.maxBashTimeouts
|
|
17880
18119
|
});
|
|
17881
18120
|
const executeTask = async (claimedTask, reporter) => {
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
|
|
18121
|
+
try {
|
|
18122
|
+
const expired = slotRegistry.reapExpiredSlots();
|
|
18123
|
+
if (expired.length > 0) rootLogger.info({
|
|
18124
|
+
expiredCount: expired.length,
|
|
18125
|
+
slotKeys: expired.map((item) => item.slot.slotKey)
|
|
18126
|
+
}, "agent-daemon.daemon_slots_reaped");
|
|
18127
|
+
} catch (err) {
|
|
18128
|
+
rootLogger.error({
|
|
18129
|
+
phase: "daemon_slot_reap",
|
|
18130
|
+
err: err instanceof Error ? err.message : String(err)
|
|
18131
|
+
}, "agent-daemon.daemon_slot_reap_failed");
|
|
18132
|
+
}
|
|
17887
18133
|
const executionPlan = executionPlans.getOrCreate(claimedTask);
|
|
17888
18134
|
if (executionPlan.slotKey && executionPlan.sessionPersistence) slotRegistry.beginSlot({
|
|
17889
18135
|
...slotIdentity,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "MoltNet agent daemon — claims and executes tasks (fulfill_brief, assess_brief) from the MoltNet task-service via Pi-headless. CLI: moltnet-agent.",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
34
34
|
"pino": "^10.3.1",
|
|
35
35
|
"pino-pretty": "^13.1.3",
|
|
36
|
-
"@themoltnet/
|
|
37
|
-
"@themoltnet/
|
|
38
|
-
"@themoltnet/
|
|
36
|
+
"@themoltnet/agent-runtime": "0.16.0",
|
|
37
|
+
"@themoltnet/sdk": "0.102.0",
|
|
38
|
+
"@themoltnet/pi-extension": "0.18.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"tsx": "^4.7.0",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"vite": "^8.0.0",
|
|
44
44
|
"vitest": "^3.0.0",
|
|
45
45
|
"@moltnet/bootstrap": "0.1.0",
|
|
46
|
+
"@moltnet/crypto-service": "0.1.0",
|
|
46
47
|
"@moltnet/database": "0.1.0",
|
|
47
|
-
"@moltnet/tasks": "0.1.0"
|
|
48
|
-
"@moltnet/crypto-service": "0.1.0"
|
|
48
|
+
"@moltnet/tasks": "0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"nx": {
|
|
51
51
|
"tags": [
|