@themoltnet/agent-daemon 0.7.0 → 0.8.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 +1 -1
- package/dist/main.js +210 -162
- package/package.json +3 -3
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
|
|
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())
|
|
@@ -6169,78 +6168,6 @@ function isHelpFlag(args) {
|
|
|
6169
6168
|
return args.includes("--help") || args.includes("-h");
|
|
6170
6169
|
}
|
|
6171
6170
|
//#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
6171
|
//#region ../../libs/agent-runtime/src/context-bindings.ts
|
|
6245
6172
|
var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
6246
6173
|
/**
|
|
@@ -6700,13 +6627,7 @@ function buildCuratePackUserPrompt(input, ctx) {
|
|
|
6700
6627
|
* is told to inspect them itself.
|
|
6701
6628
|
*/
|
|
6702
6629
|
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") : "";
|
|
6630
|
+
const { brief, title, seedFiles, scopeHint } = input;
|
|
6710
6631
|
const seedSection = seedFiles?.length ? [
|
|
6711
6632
|
"### Seed files",
|
|
6712
6633
|
"",
|
|
@@ -6754,7 +6675,6 @@ function buildFulfillBriefUserPrompt(input, ctx) {
|
|
|
6754
6675
|
"",
|
|
6755
6676
|
brief,
|
|
6756
6677
|
"",
|
|
6757
|
-
criteriaSection,
|
|
6758
6678
|
seedSection,
|
|
6759
6679
|
correlationSection,
|
|
6760
6680
|
workspaceSection,
|
|
@@ -13916,6 +13836,31 @@ function abortableSleep(ms, signal) {
|
|
|
13916
13836
|
});
|
|
13917
13837
|
}
|
|
13918
13838
|
//#endregion
|
|
13839
|
+
//#region ../../libs/agent-runtime/src/subagent-output-contracts.ts
|
|
13840
|
+
/**
|
|
13841
|
+
* Construct an immutable contract registry from a static list.
|
|
13842
|
+
*
|
|
13843
|
+
* The resulting registry is safe to share across sessions and
|
|
13844
|
+
* invocations — no mutation is possible after construction.
|
|
13845
|
+
*/
|
|
13846
|
+
function createSubagentContractRegistry(contracts) {
|
|
13847
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
13848
|
+
for (const c of contracts) {
|
|
13849
|
+
if (!c.name || c.name.trim().length === 0) throw new Error("subagent output contract name is required");
|
|
13850
|
+
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_]+)`);
|
|
13851
|
+
if (lookup.has(c.name)) throw new Error(`duplicate subagent output contract name '${c.name}' in constructor args`);
|
|
13852
|
+
lookup.set(c.name, c);
|
|
13853
|
+
}
|
|
13854
|
+
return {
|
|
13855
|
+
get(name) {
|
|
13856
|
+
return lookup.get(name) ?? null;
|
|
13857
|
+
},
|
|
13858
|
+
list() {
|
|
13859
|
+
return [...lookup.values()];
|
|
13860
|
+
}
|
|
13861
|
+
};
|
|
13862
|
+
}
|
|
13863
|
+
//#endregion
|
|
13919
13864
|
//#region ../../libs/pi-extension/src/moltnet/render-phase6.ts
|
|
13920
13865
|
function slugToTitle(value) {
|
|
13921
13866
|
return value.split(/[:/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -14892,7 +14837,8 @@ function findMainWorktree() {
|
|
|
14892
14837
|
function loadCredentials(agentDir) {
|
|
14893
14838
|
const moltnetJson = readFileSync(path.join(agentDir, "moltnet.json"), "utf8");
|
|
14894
14839
|
const agentEnvRaw = readFileSync(path.join(agentDir, "env"), "utf8");
|
|
14895
|
-
const
|
|
14840
|
+
const piAgentDir = process.env.PI_CODING_AGENT_DIR ?? path.join(process.env.HOME ?? "", ".pi", "agent");
|
|
14841
|
+
const piAuthPath = path.join(piAgentDir, "auth.json");
|
|
14896
14842
|
const piAuthJson = existsSync(piAuthPath) ? readFileSync(piAuthPath, "utf8") : null;
|
|
14897
14843
|
const gitconfigPath = path.join(agentDir, "gitconfig");
|
|
14898
14844
|
const gitconfig = existsSync(gitconfigPath) ? readFileSync(gitconfigPath, "utf8") : null;
|
|
@@ -15404,7 +15350,7 @@ async function buildAgentSession(args) {
|
|
|
15404
15350
|
spanAttributes: args.otelSpanAttrs
|
|
15405
15351
|
});
|
|
15406
15352
|
const resourceLoader = new DefaultResourceLoader({
|
|
15407
|
-
cwd: args.
|
|
15353
|
+
cwd: args.cwdPath,
|
|
15408
15354
|
agentDir: args.piAuthDir,
|
|
15409
15355
|
extensionFactories: [piOtelExtension],
|
|
15410
15356
|
appendSystemPrompt: args.appendSystemPrompt,
|
|
@@ -15412,12 +15358,12 @@ async function buildAgentSession(args) {
|
|
|
15412
15358
|
});
|
|
15413
15359
|
await resourceLoader.reload();
|
|
15414
15360
|
const sessionManager = args.sessionPersistence ? await resolvePersistentSessionManager({
|
|
15415
|
-
cwd: args.
|
|
15361
|
+
cwd: args.cwdPath,
|
|
15416
15362
|
sessionDir: args.sessionPersistence.sessionDir
|
|
15417
|
-
}) : SessionManager.inMemory(args.
|
|
15363
|
+
}) : SessionManager.inMemory(args.cwdPath);
|
|
15418
15364
|
return (await createAgentSession({
|
|
15419
15365
|
agentDir: args.piAuthDir,
|
|
15420
|
-
cwd: args.
|
|
15366
|
+
cwd: args.cwdPath,
|
|
15421
15367
|
model: args.modelHandle,
|
|
15422
15368
|
customTools: args.customTools,
|
|
15423
15369
|
sessionManager,
|
|
@@ -15662,6 +15608,7 @@ var DEFAULT_SUBAGENT_TIMEOUT_MS = 300 * 1e3;
|
|
|
15662
15608
|
*/
|
|
15663
15609
|
function createSubagentTool(args) {
|
|
15664
15610
|
const buildSession = args.buildAgentSession ?? buildAgentSession;
|
|
15611
|
+
const { contractRegistry } = args;
|
|
15665
15612
|
let callCount = 0;
|
|
15666
15613
|
return {
|
|
15667
15614
|
tool: defineTool({
|
|
@@ -15672,8 +15619,8 @@ function createSubagentTool(args) {
|
|
|
15672
15619
|
async execute(_id, params) {
|
|
15673
15620
|
if (!Check(SubagentToolParameters, params)) return toolError(`subagent: invalid parameters: ${JSON.stringify([...Errors(SubagentToolParameters, params)].slice(0, 3))}`);
|
|
15674
15621
|
const { task, output_schema } = params;
|
|
15675
|
-
const contract =
|
|
15676
|
-
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${
|
|
15622
|
+
const contract = contractRegistry.get(output_schema);
|
|
15623
|
+
if (!contract) return toolError(`subagent: unknown output_schema "${output_schema}". Registered contracts: [${contractRegistry.list().map((c) => c.name).join(", ")}]`);
|
|
15677
15624
|
callCount += 1;
|
|
15678
15625
|
const callIndex = callCount;
|
|
15679
15626
|
let captured = null;
|
|
@@ -15703,6 +15650,7 @@ function createSubagentTool(args) {
|
|
|
15703
15650
|
});
|
|
15704
15651
|
const session = await buildSession({
|
|
15705
15652
|
mountPath: args.mountPath,
|
|
15653
|
+
cwdPath: args.cwdPath ?? args.mountPath,
|
|
15706
15654
|
piAuthDir: args.piAuthDir,
|
|
15707
15655
|
modelHandle: args.modelHandle,
|
|
15708
15656
|
agentName: args.agentName,
|
|
@@ -16043,6 +15991,7 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16043
15991
|
const branch = executionPlan?.worktreeBranch ?? null;
|
|
16044
15992
|
if (!branch) return {
|
|
16045
15993
|
mountPath: requestedMountPath,
|
|
15994
|
+
cwdPath: requestedMountPath,
|
|
16046
15995
|
mode: "shared_mount",
|
|
16047
15996
|
branch: null,
|
|
16048
15997
|
cleanup: () => {}
|
|
@@ -16050,7 +15999,7 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16050
15999
|
const mainRepo = findMainWorktree();
|
|
16051
16000
|
const worktreeDir = resolveTaskWorktreePath(mainRepo, executionPlan?.workspaceId ?? `task-${task.id}`);
|
|
16052
16001
|
const relMount = relative(mainRepo, requestedMountPath);
|
|
16053
|
-
const
|
|
16002
|
+
const cwdPath = relMount === "" || relMount.startsWith("..") ? worktreeDir : join(worktreeDir, relMount);
|
|
16054
16003
|
const keepWorkspace = executionPlan?.workspaceScope === "session" && executionPlan.sessionKey !== null;
|
|
16055
16004
|
if (keepWorkspace) ensureReusableTaskWorktree(mainRepo, worktreeDir, branch);
|
|
16056
16005
|
else {
|
|
@@ -16058,7 +16007,8 @@ function prepareTaskWorkspace(task, requestedMountPath, executionPlan) {
|
|
|
16058
16007
|
addTaskWorktree(mainRepo, worktreeDir, branch);
|
|
16059
16008
|
}
|
|
16060
16009
|
return {
|
|
16061
|
-
mountPath,
|
|
16010
|
+
mountPath: mainRepo,
|
|
16011
|
+
cwdPath,
|
|
16062
16012
|
mode: "dedicated_worktree",
|
|
16063
16013
|
branch,
|
|
16064
16014
|
cleanup: keepWorkspace ? () => {} : () => {
|
|
@@ -16171,15 +16121,24 @@ var noopTurnEventHandler = () => {};
|
|
|
16171
16121
|
function createPiTaskExecutor(opts) {
|
|
16172
16122
|
let cachedCheckpoint = opts.checkpointPath ?? null;
|
|
16173
16123
|
return async (claimedTask, reporter) => {
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
})
|
|
16124
|
+
const reporterWasOpened = !reporter.cancelSignal.aborted;
|
|
16125
|
+
if (reporterWasOpened) await reporter.open({
|
|
16126
|
+
taskId: claimedTask.task.id,
|
|
16127
|
+
attemptN: claimedTask.attemptN
|
|
16179
16128
|
});
|
|
16180
16129
|
return executePiTask(claimedTask, reporter, {
|
|
16181
16130
|
...opts,
|
|
16182
|
-
checkpointPath: cachedCheckpoint
|
|
16131
|
+
checkpointPath: cachedCheckpoint ?? void 0,
|
|
16132
|
+
resolveCheckpointPath: async () => {
|
|
16133
|
+
if (!cachedCheckpoint) cachedCheckpoint = await ensureSnapshot({
|
|
16134
|
+
config: opts.sandboxConfig?.snapshot,
|
|
16135
|
+
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
16136
|
+
process.stderr.write(`[snapshot] ${m}\n`);
|
|
16137
|
+
})
|
|
16138
|
+
});
|
|
16139
|
+
return cachedCheckpoint;
|
|
16140
|
+
},
|
|
16141
|
+
reporterAlreadyOpened: reporterWasOpened
|
|
16183
16142
|
});
|
|
16184
16143
|
};
|
|
16185
16144
|
}
|
|
@@ -16195,8 +16154,9 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16195
16154
|
const startTime = Date.now();
|
|
16196
16155
|
const requestedMountPath = opts.mountPath ?? process.cwd();
|
|
16197
16156
|
const executionPlan = opts.makeExecutionPlan?.(claimedTask) ?? null;
|
|
16198
|
-
|
|
16199
|
-
|
|
16157
|
+
let workspace = null;
|
|
16158
|
+
let mountPath = requestedMountPath;
|
|
16159
|
+
let cwdPath = requestedMountPath;
|
|
16200
16160
|
if (reporter.cancelSignal.aborted) return {
|
|
16201
16161
|
taskId: task.id,
|
|
16202
16162
|
attemptN,
|
|
@@ -16211,33 +16171,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16211
16171
|
retryable: false
|
|
16212
16172
|
}
|
|
16213
16173
|
};
|
|
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 {}
|
|
16174
|
+
let reporterOpen = opts.reporterAlreadyOpened ?? false;
|
|
16230
16175
|
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
16176
|
let session = null;
|
|
16242
16177
|
let subagentHandle = null;
|
|
16243
16178
|
const finalUsage = emptyUsage(opts.provider, opts.model);
|
|
@@ -16256,41 +16191,103 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16256
16191
|
retryable: false
|
|
16257
16192
|
}
|
|
16258
16193
|
});
|
|
16194
|
+
let onTurnEvent;
|
|
16195
|
+
if (opts.makeOnTurnEvent) try {
|
|
16196
|
+
onTurnEvent = opts.makeOnTurnEvent(claimedTask);
|
|
16197
|
+
} catch (err) {
|
|
16198
|
+
process.stderr.write(`[emit] makeOnTurnEvent threw: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
16199
|
+
onTurnEvent = noopTurnEventHandler;
|
|
16200
|
+
}
|
|
16201
|
+
else onTurnEvent = opts.onTurnEvent ?? noopTurnEventHandler;
|
|
16202
|
+
const emit = (kind, payload) => {
|
|
16203
|
+
try {
|
|
16204
|
+
onTurnEvent(kind, summarizePayloadForLog(kind, payload));
|
|
16205
|
+
} catch (err) {
|
|
16206
|
+
process.stderr.write(`[emit] onTurnEvent threw for kind="${kind}": ${err instanceof Error ? err.message : String(err)}\n`);
|
|
16207
|
+
}
|
|
16208
|
+
return reporter.record({
|
|
16209
|
+
kind,
|
|
16210
|
+
payload
|
|
16211
|
+
});
|
|
16212
|
+
};
|
|
16213
|
+
const emitError = async (phase, message, extra = {}) => {
|
|
16214
|
+
await emit("error", {
|
|
16215
|
+
phase,
|
|
16216
|
+
message,
|
|
16217
|
+
...extra
|
|
16218
|
+
});
|
|
16219
|
+
};
|
|
16259
16220
|
try {
|
|
16260
|
-
|
|
16261
|
-
activateAgentEnv(managed.credentials.agentEnv, mainRepo);
|
|
16262
|
-
await reporter.open({
|
|
16221
|
+
if (!opts.reporterAlreadyOpened) await reporter.open({
|
|
16263
16222
|
taskId: task.id,
|
|
16264
16223
|
attemptN
|
|
16265
16224
|
});
|
|
16266
16225
|
reporterOpen = true;
|
|
16267
|
-
let
|
|
16268
|
-
|
|
16269
|
-
|
|
16226
|
+
let checkpointPath;
|
|
16227
|
+
try {
|
|
16228
|
+
checkpointPath = opts.checkpointPath ?? (opts.resolveCheckpointPath ? await opts.resolveCheckpointPath() : await ensureSnapshot({
|
|
16229
|
+
config: opts.sandboxConfig?.snapshot,
|
|
16230
|
+
onProgress: opts.onSnapshotProgress ?? ((m) => {
|
|
16231
|
+
process.stderr.write(`[snapshot] ${m}\n`);
|
|
16232
|
+
})
|
|
16233
|
+
}));
|
|
16270
16234
|
} catch (err) {
|
|
16271
|
-
|
|
16272
|
-
|
|
16235
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16236
|
+
await emitError("snapshot", message);
|
|
16237
|
+
return makeFailedOutput("snapshot_failed", message);
|
|
16273
16238
|
}
|
|
16274
|
-
|
|
16275
|
-
|
|
16239
|
+
try {
|
|
16240
|
+
workspace = prepareTaskWorkspace(task, requestedMountPath, executionPlan);
|
|
16241
|
+
mountPath = workspace.mountPath;
|
|
16242
|
+
cwdPath = workspace.cwdPath;
|
|
16243
|
+
} catch (err) {
|
|
16244
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16245
|
+
await emitError("worktree_setup", message);
|
|
16246
|
+
return makeFailedOutput("worktree_setup_failed", message);
|
|
16247
|
+
}
|
|
16248
|
+
try {
|
|
16249
|
+
const mainRepoForRepair = findMainWorktree();
|
|
16276
16250
|
try {
|
|
16277
|
-
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16251
|
+
execFileSync("git", [
|
|
16252
|
+
"-C",
|
|
16253
|
+
mainRepoForRepair,
|
|
16254
|
+
"worktree",
|
|
16255
|
+
"repair",
|
|
16256
|
+
"--relative-paths"
|
|
16257
|
+
], { stdio: "pipe" });
|
|
16258
|
+
} catch {}
|
|
16259
|
+
} catch (err) {
|
|
16260
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16261
|
+
await emitError("worktree_setup", message);
|
|
16262
|
+
return makeFailedOutput("worktree_setup_failed", message);
|
|
16263
|
+
}
|
|
16264
|
+
try {
|
|
16265
|
+
managed = await resumeVm({
|
|
16266
|
+
checkpointPath,
|
|
16267
|
+
agentName: opts.agentName,
|
|
16268
|
+
mountPath,
|
|
16269
|
+
extraAllowedHosts: opts.extraAllowedHosts,
|
|
16270
|
+
sandboxConfig: opts.sandboxConfig
|
|
16284
16271
|
});
|
|
16285
|
-
}
|
|
16272
|
+
} catch (err) {
|
|
16273
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16274
|
+
await emitError("vm_resume", message);
|
|
16275
|
+
return makeFailedOutput("vm_resume_failed", message);
|
|
16276
|
+
}
|
|
16277
|
+
const diaryId = task.diaryId ?? "";
|
|
16278
|
+
const taskTeamId = task.teamId ?? "";
|
|
16279
|
+
const mainRepo = findMainWorktree();
|
|
16280
|
+
activateAgentEnv(managed.credentials.agentEnv, mainRepo);
|
|
16281
|
+
const activeWorkspace = workspace;
|
|
16282
|
+
if (!activeWorkspace) throw new Error("task workspace not prepared");
|
|
16286
16283
|
await emit("info", {
|
|
16287
16284
|
event: "execute_start",
|
|
16288
16285
|
taskType: task.taskType,
|
|
16289
16286
|
teamId: task.teamId,
|
|
16290
16287
|
provider: opts.provider,
|
|
16291
16288
|
model: opts.model,
|
|
16292
|
-
workspaceMode:
|
|
16293
|
-
workspaceBranch:
|
|
16289
|
+
workspaceMode: activeWorkspace.mode,
|
|
16290
|
+
workspaceBranch: activeWorkspace.branch
|
|
16294
16291
|
});
|
|
16295
16292
|
let taskPrompt;
|
|
16296
16293
|
try {
|
|
@@ -16298,8 +16295,8 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16298
16295
|
diaryId,
|
|
16299
16296
|
taskId: task.id,
|
|
16300
16297
|
workspace: {
|
|
16301
|
-
mode:
|
|
16302
|
-
branch:
|
|
16298
|
+
mode: activeWorkspace.mode,
|
|
16299
|
+
branch: activeWorkspace.branch
|
|
16303
16300
|
},
|
|
16304
16301
|
extras: opts.promptExtras
|
|
16305
16302
|
});
|
|
@@ -16351,7 +16348,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16351
16348
|
getTeamId: () => taskTeamId,
|
|
16352
16349
|
getSessionErrors: () => [],
|
|
16353
16350
|
clearSessionErrors: () => {},
|
|
16354
|
-
getHostCwd: () =>
|
|
16351
|
+
getHostCwd: () => cwdPath,
|
|
16355
16352
|
hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
|
|
16356
16353
|
hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
|
|
16357
16354
|
getTaskContext: () => ({
|
|
@@ -16379,6 +16376,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16379
16376
|
if (taskTypeUsesSubagents(task.taskType)) {
|
|
16380
16377
|
subagentHandle = createSubagentTool({
|
|
16381
16378
|
mountPath,
|
|
16379
|
+
cwdPath,
|
|
16382
16380
|
piAuthDir,
|
|
16383
16381
|
modelHandle,
|
|
16384
16382
|
agentName: opts.agentName,
|
|
@@ -16387,12 +16385,14 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16387
16385
|
parentTaskId: task.id,
|
|
16388
16386
|
parentTaskType: task.taskType,
|
|
16389
16387
|
parentAttemptN: attemptN,
|
|
16388
|
+
contractRegistry: opts.subagentContractRegistry,
|
|
16390
16389
|
parentCancelSignal: reporter.cancelSignal
|
|
16391
16390
|
});
|
|
16392
16391
|
parentSubagentTools.push(subagentHandle.tool);
|
|
16393
16392
|
}
|
|
16394
16393
|
session = await buildAgentSession({
|
|
16395
16394
|
mountPath,
|
|
16395
|
+
cwdPath,
|
|
16396
16396
|
piAuthDir,
|
|
16397
16397
|
modelHandle,
|
|
16398
16398
|
agentName: opts.agentName,
|
|
@@ -16477,6 +16477,10 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16477
16477
|
is_error: event.isError,
|
|
16478
16478
|
result: event.isError ? truncateForWire(event.result) : void 0
|
|
16479
16479
|
}));
|
|
16480
|
+
if (event.isError) track(emitError("tool_call_error", describeToolErrorMessage(event.result), {
|
|
16481
|
+
tool: event.toolName,
|
|
16482
|
+
result: truncateForWire(event.result)
|
|
16483
|
+
}));
|
|
16480
16484
|
if (maxBashTimeouts > 0 && event.toolName === "bash" && event.isError && isBashTimeoutResult(event.result)) {
|
|
16481
16485
|
bashTimeoutCount += 1;
|
|
16482
16486
|
if (bashTimeoutCount >= maxBashTimeouts) triggerCapAbort("max_bash_timeouts_exceeded", `Aborted after ${bashTimeoutCount} bash timeouts in this attempt (cap ${maxBashTimeouts}).`);
|
|
@@ -16632,7 +16636,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
16632
16636
|
}
|
|
16633
16637
|
}
|
|
16634
16638
|
if (managed) await managed.vm.close();
|
|
16635
|
-
try {
|
|
16639
|
+
if (workspace) try {
|
|
16636
16640
|
workspace.cleanup();
|
|
16637
16641
|
} catch (err) {
|
|
16638
16642
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -16743,6 +16747,23 @@ function truncateForWire(value) {
|
|
|
16743
16747
|
};
|
|
16744
16748
|
}
|
|
16745
16749
|
}
|
|
16750
|
+
function describeToolErrorMessage(result) {
|
|
16751
|
+
if (typeof result === "string" && result.trim().length > 0) return result.trim();
|
|
16752
|
+
if (result && typeof result === "object") {
|
|
16753
|
+
const content = result.content;
|
|
16754
|
+
if (Array.isArray(content)) {
|
|
16755
|
+
for (const item of content) if (item && typeof item === "object" && typeof item.text === "string") {
|
|
16756
|
+
const text = item.text.trim();
|
|
16757
|
+
if (text.length > 0) return text;
|
|
16758
|
+
}
|
|
16759
|
+
}
|
|
16760
|
+
}
|
|
16761
|
+
try {
|
|
16762
|
+
return JSON.stringify(truncateForWire(result));
|
|
16763
|
+
} catch {
|
|
16764
|
+
return "Tool call failed";
|
|
16765
|
+
}
|
|
16766
|
+
}
|
|
16746
16767
|
//#endregion
|
|
16747
16768
|
//#region src/config.ts
|
|
16748
16769
|
function loadConfig() {
|
|
@@ -17619,9 +17640,15 @@ async function runPolling(opts) {
|
|
|
17619
17640
|
maxPollIntervalMs
|
|
17620
17641
|
}, "agent-daemon.starting");
|
|
17621
17642
|
const outputs = [];
|
|
17643
|
+
const subagentContractRegistry = createSubagentContractRegistry([{
|
|
17644
|
+
name: "judge_eval_variant_result",
|
|
17645
|
+
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.",
|
|
17646
|
+
parametersSchema: JudgeEvalVariantResult
|
|
17647
|
+
}]);
|
|
17622
17648
|
try {
|
|
17623
17649
|
const executeTask = createPiTaskExecutor({
|
|
17624
17650
|
agentName: common.agent,
|
|
17651
|
+
subagentContractRegistry,
|
|
17625
17652
|
mountPath: sandbox.rootDir,
|
|
17626
17653
|
provider: common.provider,
|
|
17627
17654
|
model: common.model,
|
|
@@ -17667,11 +17694,19 @@ async function runPolling(opts) {
|
|
|
17667
17694
|
executeTask: async (claimedTask, reporter) => {
|
|
17668
17695
|
const executionPlan = executionPlans.getOrCreate(claimedTask);
|
|
17669
17696
|
const sessionDescriptor = executionPlan.descriptor;
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
|
|
17697
|
+
let expired;
|
|
17698
|
+
try {
|
|
17699
|
+
expired = slotRegistry.reapExpiredSlots();
|
|
17700
|
+
if (expired.length > 0) rootLogger.info({
|
|
17701
|
+
expiredCount: expired.length,
|
|
17702
|
+
slotKeys: expired.map((item) => item.slot.slotKey)
|
|
17703
|
+
}, "agent-daemon.daemon_slots_reaped");
|
|
17704
|
+
} catch (err) {
|
|
17705
|
+
rootLogger.error({
|
|
17706
|
+
phase: "daemon_slot_reap",
|
|
17707
|
+
err: err instanceof Error ? err.message : String(err)
|
|
17708
|
+
}, "agent-daemon.daemon_slot_reap_failed");
|
|
17709
|
+
}
|
|
17675
17710
|
rootLogger.debug({
|
|
17676
17711
|
taskId: claimedTask.task.id,
|
|
17677
17712
|
taskType: claimedTask.task.taskType,
|
|
@@ -17866,9 +17901,15 @@ async function runOnce(argv) {
|
|
|
17866
17901
|
process.on("SIGTERM", () => {
|
|
17867
17902
|
onSignal("SIGTERM");
|
|
17868
17903
|
});
|
|
17904
|
+
const subagentContractRegistry = createSubagentContractRegistry([{
|
|
17905
|
+
name: "judge_eval_variant_result",
|
|
17906
|
+
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.",
|
|
17907
|
+
parametersSchema: JudgeEvalVariantResult
|
|
17908
|
+
}]);
|
|
17869
17909
|
try {
|
|
17870
17910
|
const rawExecuteTask = createPiTaskExecutor({
|
|
17871
17911
|
agentName: opts.agent,
|
|
17912
|
+
subagentContractRegistry,
|
|
17872
17913
|
mountPath: sandbox.rootDir,
|
|
17873
17914
|
provider: opts.provider,
|
|
17874
17915
|
model: opts.model,
|
|
@@ -17879,11 +17920,18 @@ async function runOnce(argv) {
|
|
|
17879
17920
|
maxBashTimeouts: opts.maxBashTimeouts
|
|
17880
17921
|
});
|
|
17881
17922
|
const executeTask = async (claimedTask, reporter) => {
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
|
|
17885
|
-
|
|
17886
|
-
|
|
17923
|
+
try {
|
|
17924
|
+
const expired = slotRegistry.reapExpiredSlots();
|
|
17925
|
+
if (expired.length > 0) rootLogger.info({
|
|
17926
|
+
expiredCount: expired.length,
|
|
17927
|
+
slotKeys: expired.map((item) => item.slot.slotKey)
|
|
17928
|
+
}, "agent-daemon.daemon_slots_reaped");
|
|
17929
|
+
} catch (err) {
|
|
17930
|
+
rootLogger.error({
|
|
17931
|
+
phase: "daemon_slot_reap",
|
|
17932
|
+
err: err instanceof Error ? err.message : String(err)
|
|
17933
|
+
}, "agent-daemon.daemon_slot_reap_failed");
|
|
17934
|
+
}
|
|
17887
17935
|
const executionPlan = executionPlans.getOrCreate(claimedTask);
|
|
17888
17936
|
if (executionPlan.slotKey && executionPlan.sessionPersistence) slotRegistry.beginSlot({
|
|
17889
17937
|
...slotIdentity,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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,8 +33,8 @@
|
|
|
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/
|
|
36
|
+
"@themoltnet/agent-runtime": "0.15.2",
|
|
37
|
+
"@themoltnet/pi-extension": "0.18.0",
|
|
38
38
|
"@themoltnet/sdk": "0.102.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|