@themoltnet/agent-daemon 0.35.0 → 0.36.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/cli.js +73 -28
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -253,7 +253,7 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
253
253
|
fragments: {
|
|
254
254
|
"artifact-planner-v1": {
|
|
255
255
|
binding: "prompt_prefix",
|
|
256
|
-
content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts,
|
|
256
|
+
content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts, modify a checkout, commit, branch, push, or contact GitHub.\n- Read only the exact artifact CIDs named by the task, and only when the embedded manifest does not provide enough evidence. Use the registered task-artifact tools for artifact access; never use shell or CLI wrappers to fetch artifacts, paginate, or discover them speculatively.\n- If the effective runtime exposes a local calculator or shell, use it only inside scratch for coverage accounting, budget arithmetic, and JSON validation. The runtime capability section and policy are authoritative; do not assume a static executable list.\n- Perform semantic classification and planning from supplied content and producer/consumer evidence. Do not substitute filename, directory, language, ecosystem, or repository-specific exclusion rules for evidence.\n- Write and upload exactly the requested versioned plan artifact, then reference its returned metadata through the registered submit-output tool. Do not emit a second prose or JSON representation.",
|
|
257
257
|
slug: "artifact-planner-v1"
|
|
258
258
|
},
|
|
259
259
|
"accountable-delivery-v1": {
|
|
@@ -283,7 +283,7 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
283
283
|
},
|
|
284
284
|
"verification-and-artifacts-v1": {
|
|
285
285
|
binding: "prompt_prefix",
|
|
286
|
-
content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload
|
|
286
|
+
content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload only task-relevant artifacts, and inspect each before uploading. Never upload secrets, credentials, API keys, auth tokens or headers, .env files, or personal or customer data; redact sensitive values, and prefer minimal, sanitized excerpts over whole logs, bundles, or datasets. Include artifact metadata only where the typed submit contract permits it.\n- If the task depends on prior artifacts, list and download the exact referenced artifact before judging or continuing that work.",
|
|
287
287
|
slug: "verification-and-artifacts-v1"
|
|
288
288
|
}
|
|
289
289
|
},
|
|
@@ -2538,11 +2538,14 @@ var CuratePackOutput = Type.Object({
|
|
|
2538
2538
|
//#endregion
|
|
2539
2539
|
//#region ../../libs/tasks/src/task-types/freeform.ts
|
|
2540
2540
|
var FREEFORM_TYPE = "freeform";
|
|
2541
|
-
var FreeformExecutionOptions = Type.Object({
|
|
2542
|
-
Type.
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2541
|
+
var FreeformExecutionOptions = Type.Object({
|
|
2542
|
+
workspace: Type.Optional(Type.Union([
|
|
2543
|
+
Type.Literal("none"),
|
|
2544
|
+
Type.Literal("shared_mount"),
|
|
2545
|
+
Type.Literal("dedicated_worktree")
|
|
2546
|
+
])),
|
|
2547
|
+
revision: Type.Optional(Type.String({ pattern: "^[0-9a-fA-F]{40}$" }))
|
|
2548
|
+
}, {
|
|
2546
2549
|
$id: "FreeformExecutionOptions",
|
|
2547
2550
|
additionalProperties: false
|
|
2548
2551
|
});
|
|
@@ -2628,6 +2631,12 @@ var FreeformOutput = Type.Object({
|
|
|
2628
2631
|
* are sequential preconditions, later ones presume earlier ones hold.
|
|
2629
2632
|
*/
|
|
2630
2633
|
async function validateFreeformInputAsync(input, ctx) {
|
|
2634
|
+
const execution = input.execution;
|
|
2635
|
+
if (execution?.revision && execution.workspace === "none") return [{
|
|
2636
|
+
field: "input/execution/revision",
|
|
2637
|
+
message: "execution.revision requires a repository workspace; use shared_mount or dedicated_worktree",
|
|
2638
|
+
code: "freeform.executionRevisionRequiresRepository"
|
|
2639
|
+
}];
|
|
2631
2640
|
const cf = input.continueFrom;
|
|
2632
2641
|
if (!cf) return [];
|
|
2633
2642
|
const source = await ctx.resolveTask(cf.taskId);
|
|
@@ -2641,11 +2650,16 @@ async function validateFreeformInputAsync(input, ctx) {
|
|
|
2641
2650
|
message: `Source task type '${source.taskType}' is not continuable; only freeform → freeform is supported in v1`,
|
|
2642
2651
|
code: "freeform.sourceTaskTypeNotSupported"
|
|
2643
2652
|
}];
|
|
2644
|
-
if (
|
|
2653
|
+
if (execution?.workspace) return [{
|
|
2645
2654
|
field: "input/execution/workspace",
|
|
2646
2655
|
message: "execution.workspace is derived from parent runtime context when continueFrom is set; omit it",
|
|
2647
2656
|
code: "freeform.executionWorkspaceNotInheritable"
|
|
2648
2657
|
}];
|
|
2658
|
+
if (execution?.revision) return [{
|
|
2659
|
+
field: "input/execution/revision",
|
|
2660
|
+
message: "execution.revision is derived from parent runtime context when continueFrom is set; omit it",
|
|
2661
|
+
code: "freeform.executionRevisionNotInheritable"
|
|
2662
|
+
}];
|
|
2649
2663
|
if (ctx.deferReadinessChecks) return [];
|
|
2650
2664
|
const attempt = (await ctx.listAttempts(cf.taskId)).find((a) => a.attemptN === cf.attemptN);
|
|
2651
2665
|
if (!attempt || attempt.status !== "completed") return [{
|
|
@@ -4246,6 +4260,7 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
|
|
|
4246
4260
|
const slotId = slotKey ? buildDaemonSlotId(identity, slotKey) : null;
|
|
4247
4261
|
const sessionDir = slotId ? `${stateDirs.piSessionsDir}/${boundedKeyDirComponent(slotId)}` : null;
|
|
4248
4262
|
const worktreeBranch = resolveTaskWorktreeBranch(task, workspaceMode);
|
|
4263
|
+
const workspaceRevision = resolveTaskWorkspaceRevision(task.input);
|
|
4249
4264
|
const workspaceId = workspaceMode !== "shared_mount" ? resolveTaskWorkspaceId(task, {
|
|
4250
4265
|
sessionKey: slotId,
|
|
4251
4266
|
workspaceScope,
|
|
@@ -4261,9 +4276,14 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
|
|
|
4261
4276
|
workspaceScope,
|
|
4262
4277
|
sessionPersistence: sessionDir ? { sessionDir } : null,
|
|
4263
4278
|
workspaceId,
|
|
4264
|
-
worktreeBranch
|
|
4279
|
+
worktreeBranch,
|
|
4280
|
+
workspaceRevision
|
|
4265
4281
|
};
|
|
4266
4282
|
}
|
|
4283
|
+
function resolveTaskWorkspaceRevision(input) {
|
|
4284
|
+
const value = input?.execution?.revision;
|
|
4285
|
+
return typeof value === "string" && /^[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
|
|
4286
|
+
}
|
|
4267
4287
|
function buildDaemonSlotId(identity, slotKey) {
|
|
4268
4288
|
return [
|
|
4269
4289
|
"agent",
|
|
@@ -4289,6 +4309,7 @@ function slugSlotIdentityComponent(input) {
|
|
|
4289
4309
|
}
|
|
4290
4310
|
function resolveTaskWorktreeBranch(task, workspaceMode) {
|
|
4291
4311
|
if (workspaceMode !== "dedicated_worktree") return null;
|
|
4312
|
+
if (typeof task.input.execution?.revision === "string") return null;
|
|
4292
4313
|
if (task.taskType === "fulfill_brief") {
|
|
4293
4314
|
const input = task.input;
|
|
4294
4315
|
const slug = slugifyAsciiLower(typeof task.title === "string" && task.title.trim().length > 0 ? task.title : typeof input.brief === "string" && input.brief.trim().length > 0 ? input.brief : task.taskType, 60) || "task";
|
|
@@ -4383,9 +4404,14 @@ function createExecutionPlanCache(args) {
|
|
|
4383
4404
|
};
|
|
4384
4405
|
}
|
|
4385
4406
|
function createNullSourceAttemptResolver() {
|
|
4386
|
-
return {
|
|
4387
|
-
|
|
4388
|
-
|
|
4407
|
+
return {
|
|
4408
|
+
findOutputBranch() {
|
|
4409
|
+
return Promise.resolve(null);
|
|
4410
|
+
},
|
|
4411
|
+
findInputRevision() {
|
|
4412
|
+
return Promise.resolve(null);
|
|
4413
|
+
}
|
|
4414
|
+
};
|
|
4389
4415
|
}
|
|
4390
4416
|
function createNullRuntimeSessionStore() {
|
|
4391
4417
|
return {
|
|
@@ -4409,7 +4435,7 @@ function assertPlanAllowedByWorkspacePolicy(plan, policy) {
|
|
|
4409
4435
|
}
|
|
4410
4436
|
function planToRuntimeProfileWorkspaceMode(plan) {
|
|
4411
4437
|
if (plan.workspaceMode === "scratch_mount") return "none";
|
|
4412
|
-
if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch) return "shared_mount";
|
|
4438
|
+
if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch && !plan.workspaceRevision) return "shared_mount";
|
|
4413
4439
|
return plan.workspaceMode;
|
|
4414
4440
|
}
|
|
4415
4441
|
function buildClaimedTaskKey(task) {
|
|
@@ -4455,16 +4481,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4455
4481
|
attemptN: continueFrom.attemptN,
|
|
4456
4482
|
taskId: continueFrom.taskId
|
|
4457
4483
|
});
|
|
4484
|
+
const recoveredRevision = recoveredBranch ? null : await sourceAttemptResolver.findInputRevision({
|
|
4485
|
+
attemptN: continueFrom.attemptN,
|
|
4486
|
+
taskId: continueFrom.taskId
|
|
4487
|
+
});
|
|
4458
4488
|
if (continueFrom.mode === "fork") {
|
|
4459
|
-
if (recoveredBranch) {
|
|
4489
|
+
if (recoveredBranch || recoveredRevision) {
|
|
4460
4490
|
const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
4461
|
-
const forkBranch = buildForkBranch(recoveredBranch, claimedTask.task.id, claimedTask.attemptN);
|
|
4491
|
+
const forkBranch = buildForkBranch(recoveredBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
|
|
4462
4492
|
return {
|
|
4463
4493
|
...basePlan,
|
|
4464
4494
|
workspaceMode: "dedicated_worktree",
|
|
4465
4495
|
workspaceId: forkWorkspaceId,
|
|
4466
4496
|
worktreeBranch: forkBranch,
|
|
4467
|
-
worktreeBaseRef: recoveredBranch,
|
|
4497
|
+
worktreeBaseRef: recoveredBranch ?? recoveredRevision,
|
|
4468
4498
|
workspaceKind: "fork",
|
|
4469
4499
|
sessionPersistence: {
|
|
4470
4500
|
sessionDir,
|
|
@@ -4474,11 +4504,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4474
4504
|
}
|
|
4475
4505
|
throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: durable runtime session is available but the source attempt output did not report a branch`);
|
|
4476
4506
|
}
|
|
4507
|
+
const hasDedicatedWorkspace = Boolean(recoveredBranch || recoveredRevision);
|
|
4477
4508
|
return {
|
|
4478
4509
|
...basePlan,
|
|
4479
|
-
workspaceMode: "dedicated_worktree",
|
|
4480
|
-
workspaceId:
|
|
4510
|
+
workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
|
|
4511
|
+
workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
|
|
4481
4512
|
worktreeBranch: recoveredBranch,
|
|
4513
|
+
workspaceRevision: recoveredRevision,
|
|
4482
4514
|
sessionPersistence: {
|
|
4483
4515
|
sessionDir,
|
|
4484
4516
|
forkFromSessionPath: resolution.sessionPath
|
|
@@ -4486,16 +4518,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4486
4518
|
};
|
|
4487
4519
|
}
|
|
4488
4520
|
const parentBranch = resolution.producerSlot.workspace?.worktreeBranch ?? null;
|
|
4521
|
+
const parentRevision = parentBranch ? null : await sourceAttemptResolver.findInputRevision({
|
|
4522
|
+
attemptN: continueFrom.attemptN,
|
|
4523
|
+
taskId: continueFrom.taskId
|
|
4524
|
+
});
|
|
4489
4525
|
if (continueFrom.mode === "fork") {
|
|
4490
|
-
if (!parentBranch) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch to fork from`);
|
|
4526
|
+
if (!parentBranch && !parentRevision) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch or immutable revision to fork from`);
|
|
4491
4527
|
const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
4492
|
-
const forkBranch = buildForkBranch(parentBranch, claimedTask.task.id, claimedTask.attemptN);
|
|
4528
|
+
const forkBranch = buildForkBranch(parentBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
|
|
4493
4529
|
return {
|
|
4494
4530
|
...basePlan,
|
|
4495
4531
|
workspaceMode: "dedicated_worktree",
|
|
4496
4532
|
workspaceId: forkWorkspaceId,
|
|
4497
4533
|
worktreeBranch: forkBranch,
|
|
4498
|
-
worktreeBaseRef: parentBranch,
|
|
4534
|
+
worktreeBaseRef: parentBranch ?? parentRevision,
|
|
4499
4535
|
workspaceKind: "fork",
|
|
4500
4536
|
sessionPersistence: {
|
|
4501
4537
|
sessionDir,
|
|
@@ -4503,11 +4539,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4503
4539
|
}
|
|
4504
4540
|
};
|
|
4505
4541
|
}
|
|
4542
|
+
const hasDedicatedWorkspace = Boolean(parentBranch || parentRevision);
|
|
4506
4543
|
return {
|
|
4507
4544
|
...basePlan,
|
|
4508
|
-
workspaceMode: "dedicated_worktree",
|
|
4509
|
-
workspaceId:
|
|
4545
|
+
workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
|
|
4546
|
+
workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
|
|
4510
4547
|
worktreeBranch: parentBranch,
|
|
4548
|
+
workspaceRevision: parentRevision,
|
|
4511
4549
|
sessionPersistence: {
|
|
4512
4550
|
sessionDir,
|
|
4513
4551
|
forkFromSessionPath: resolution.sessionPath
|
|
@@ -5702,11 +5740,18 @@ function signalExitCode(signal) {
|
|
|
5702
5740
|
//#region src/lib/source-attempts.ts
|
|
5703
5741
|
function createApiSourceAttemptResolver(args) {
|
|
5704
5742
|
const { agent } = args;
|
|
5705
|
-
return {
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5743
|
+
return {
|
|
5744
|
+
async findOutputBranch(input) {
|
|
5745
|
+
const attempt = (await agent.tasks.listAttempts(input.taskId)).find((candidate) => candidate.attemptN === input.attemptN);
|
|
5746
|
+
if (!attempt || attempt.status !== "completed") return null;
|
|
5747
|
+
return resolveOutputBranch(attempt.output);
|
|
5748
|
+
},
|
|
5749
|
+
async findInputRevision(input) {
|
|
5750
|
+
const task = await agent.tasks.get(input.taskId);
|
|
5751
|
+
if (task.status !== "completed" || task.acceptedAttemptN !== input.attemptN) return null;
|
|
5752
|
+
return resolveTaskWorkspaceRevision(task.input);
|
|
5753
|
+
}
|
|
5754
|
+
};
|
|
5710
5755
|
}
|
|
5711
5756
|
function resolveOutputBranch(output) {
|
|
5712
5757
|
if (!output || typeof output !== "object") return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Universal MoltNet agent daemon host with a built-in Pi/Gondolin runtime and support for trusted operator-owned runtime modules. CLI: moltnet-agent.",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"pino": "^10.3.1",
|
|
63
63
|
"pino-pretty": "^13.1.3",
|
|
64
64
|
"typebox": "^1.2.8",
|
|
65
|
-
"@themoltnet/
|
|
66
|
-
"@themoltnet/
|
|
65
|
+
"@themoltnet/pi-runtime": "0.6.0",
|
|
66
|
+
"@themoltnet/agent-runtime": "0.40.0",
|
|
67
67
|
"@themoltnet/sdk": "0.128.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"vitest": "^3.0.0",
|
|
75
75
|
"@moltnet/bootstrap": "0.1.0",
|
|
76
76
|
"@moltnet/crypto-service": "0.1.0",
|
|
77
|
-
"@moltnet/
|
|
78
|
-
"@moltnet/
|
|
77
|
+
"@moltnet/tasks": "0.1.0",
|
|
78
|
+
"@moltnet/observability": "0.1.0"
|
|
79
79
|
},
|
|
80
80
|
"nx": {
|
|
81
81
|
"projectType": "application",
|