@themoltnet/agent-daemon 0.10.6 → 0.11.1
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 +7 -2
- package/dist/main.js +223 -121
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ The package ships a single binary: `moltnet-agent`.
|
|
|
27
27
|
```bash
|
|
28
28
|
moltnet-agent once --task-id <uuid>
|
|
29
29
|
moltnet-agent poll --task-types fulfill_brief,assess_brief
|
|
30
|
+
moltnet-agent poll --task-types freeform
|
|
30
31
|
moltnet-agent drain
|
|
31
32
|
```
|
|
32
33
|
|
|
@@ -112,6 +113,10 @@ verifying changes that touch prompt assembly, tool wiring, or task lifecycle.
|
|
|
112
113
|
**Not** an automated CI flow — each run spends real model tokens and boots a
|
|
113
114
|
Gondolin VM, which is why we keep it manual.
|
|
114
115
|
|
|
116
|
+
`freeform` is useful for smoke-testing generic prompt/output plumbing because it
|
|
117
|
+
does not require a domain-specific producer or judge setup. It is still a
|
|
118
|
+
registered task type; unknown task-type names remain invalid.
|
|
119
|
+
|
|
115
120
|
### Prerequisites
|
|
116
121
|
|
|
117
122
|
- Docker running.
|
|
@@ -250,7 +255,7 @@ Leave it running. It idles until a task lands in its queue.
|
|
|
250
255
|
|
|
251
256
|
In another terminal, with `.moltnet/local-dev/env` sourced. Pick the CLI
|
|
252
257
|
form (recommended — schema-validates locally, no Node dependency in the
|
|
253
|
-
|
|
258
|
+
proposer path) or the template-driven TS form (legacy path that supports
|
|
254
259
|
`{{placeholder}}` substitution via `--set`).
|
|
255
260
|
|
|
256
261
|
::: code-group
|
|
@@ -325,7 +330,7 @@ pnpm exec tsx tools/src/tasks/create-pr-review.ts \
|
|
|
325
330
|
--repo <owner/repo>
|
|
326
331
|
```
|
|
327
332
|
|
|
328
|
-
This helper stays
|
|
333
|
+
This helper stays proposer-only. It reads PR metadata, ensures the PR
|
|
329
334
|
correlation marker exists, loads the binary rubric, and creates the
|
|
330
335
|
`pr_review` task. The daemon-claimed LLM attempt remains responsible for
|
|
331
336
|
the review itself and for any requested outward action such as `gh pr comment`.
|
package/dist/main.js
CHANGED
|
@@ -2901,7 +2901,7 @@ var ContextBinding = Type$2.Union([
|
|
|
2901
2901
|
Type$2.Literal("user_inline")
|
|
2902
2902
|
], { $id: "ContextBinding" });
|
|
2903
2903
|
/**
|
|
2904
|
-
* One context entry. Bytes are inlined: the
|
|
2904
|
+
* One context entry. Bytes are inlined: the proposer chose them, and the
|
|
2905
2905
|
* task's `inputCid` already pins the entire input — including
|
|
2906
2906
|
* `context[]` — so we don't need a separate per-entry hash, fetcher, or
|
|
2907
2907
|
* flagged-content gate. Tasks reference rendered packs (or any other
|
|
@@ -3047,7 +3047,7 @@ function validateRubricWeights(rubric) {
|
|
|
3047
3047
|
//#endregion
|
|
3048
3048
|
//#region ../../libs/tasks/src/success-criteria.ts
|
|
3049
3049
|
/**
|
|
3050
|
-
* SuccessCriteria —
|
|
3050
|
+
* SuccessCriteria — proposer-stated acceptance criteria, evaluated in two
|
|
3051
3051
|
* complementary places.
|
|
3052
3052
|
*
|
|
3053
3053
|
* Before this envelope existed, criteria were scattered: a vestigial
|
|
@@ -3056,7 +3056,7 @@ function validateRubricWeights(rubric) {
|
|
|
3056
3056
|
* judgment-task inputs. None of those were machine-verifiable
|
|
3057
3057
|
* end-to-end.
|
|
3058
3058
|
*
|
|
3059
|
-
* This module defines a single, content-addressable envelope
|
|
3059
|
+
* This module defines a single, content-addressable envelope a proposer
|
|
3060
3060
|
* attaches to any task type. It has four orthogonal sections — pick
|
|
3061
3061
|
* whichever apply per task type:
|
|
3062
3062
|
*
|
|
@@ -3090,7 +3090,7 @@ function validateRubricWeights(rubric) {
|
|
|
3090
3090
|
* spec for the judge.
|
|
3091
3091
|
*
|
|
3092
3092
|
* The clean chain: producer task with `successCriteria` → producer
|
|
3093
|
-
* self-assesses honestly →
|
|
3093
|
+
* self-assesses honestly → proposer (or automation) creates a downstream
|
|
3094
3094
|
* judgment task that references the same `successCriteria` (or a
|
|
3095
3095
|
* stricter rubric) → judgment task delivers the binding verdict.
|
|
3096
3096
|
*
|
|
@@ -4093,8 +4093,9 @@ var AssessBriefOutput = Type$2.Object({
|
|
|
4093
4093
|
* - `targetTaskId` resolves to a real task the caller can see.
|
|
4094
4094
|
* - The target is a `fulfill_brief` (you cannot grade an arbitrary
|
|
4095
4095
|
* task type as if it were a brief fulfillment).
|
|
4096
|
-
* -
|
|
4097
|
-
* an
|
|
4096
|
+
* - Unless readiness checks are explicitly deferred, the target is
|
|
4097
|
+
* `completed` with an accepted attempt — grading an in-flight or
|
|
4098
|
+
* failed task would either race or grade nothing.
|
|
4098
4099
|
*
|
|
4099
4100
|
* Agent-distinctness ("assessor ≠ producer") is a runtime / auth-
|
|
4100
4101
|
* layer concern and intentionally NOT checked here. It belongs in
|
|
@@ -4115,7 +4116,7 @@ async function validateAssessBriefInputAsync(input, ctx) {
|
|
|
4115
4116
|
field: "targetTaskId",
|
|
4116
4117
|
message: `targetTaskId ${targetTaskId} is a ${target.taskType}, not a fulfill_brief`
|
|
4117
4118
|
});
|
|
4118
|
-
if (target.status !== "completed" || target.acceptedAttemptN === null) errors.push({
|
|
4119
|
+
if (!ctx.deferReadinessChecks && (target.status !== "completed" || target.acceptedAttemptN === null)) errors.push({
|
|
4119
4120
|
field: "targetTaskId",
|
|
4120
4121
|
message: `targetTaskId ${targetTaskId} is not completed with an accepted attempt (status=${target.status}, acceptedAttemptN=${target.acceptedAttemptN})`
|
|
4121
4122
|
});
|
|
@@ -4189,6 +4190,59 @@ var CuratePackOutput = Type$2.Object({
|
|
|
4189
4190
|
additionalProperties: false
|
|
4190
4191
|
});
|
|
4191
4192
|
//#endregion
|
|
4193
|
+
//#region ../../libs/tasks/src/task-types/freeform.ts
|
|
4194
|
+
var FREEFORM_TYPE = "freeform";
|
|
4195
|
+
var FreeformTaskTypeProposal = Type$2.Object({
|
|
4196
|
+
name: Type$2.String({ minLength: 1 }),
|
|
4197
|
+
rationale: Type$2.String({ minLength: 1 }),
|
|
4198
|
+
inputShape: Type$2.Optional(Type$2.Record(Type$2.String(), Type$2.Unknown())),
|
|
4199
|
+
outputShape: Type$2.Optional(Type$2.Record(Type$2.String(), Type$2.Unknown()))
|
|
4200
|
+
}, {
|
|
4201
|
+
$id: "FreeformTaskTypeProposal",
|
|
4202
|
+
additionalProperties: false
|
|
4203
|
+
});
|
|
4204
|
+
var FreeformInput = Type$2.Object({
|
|
4205
|
+
title: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4206
|
+
brief: Type$2.String({ minLength: 1 }),
|
|
4207
|
+
expectedOutput: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4208
|
+
constraints: Type$2.Optional(Type$2.Array(Type$2.String({ minLength: 1 }), { maxItems: 20 })),
|
|
4209
|
+
suggestedTaskType: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4210
|
+
successCriteria: Type$2.Optional(SuccessCriteria),
|
|
4211
|
+
context: Type$2.Optional(TaskContext)
|
|
4212
|
+
}, {
|
|
4213
|
+
$id: "FreeformInput",
|
|
4214
|
+
additionalProperties: false
|
|
4215
|
+
});
|
|
4216
|
+
var FreeformArtifact = Type$2.Object({
|
|
4217
|
+
kind: Type$2.String({ minLength: 1 }),
|
|
4218
|
+
title: Type$2.String({ minLength: 1 }),
|
|
4219
|
+
description: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4220
|
+
url: Type$2.Optional(Type$2.String({ minLength: 1 })),
|
|
4221
|
+
path: Type$2.Optional(Type$2.String({ minLength: 1 }))
|
|
4222
|
+
}, {
|
|
4223
|
+
$id: "FreeformArtifact",
|
|
4224
|
+
additionalProperties: false
|
|
4225
|
+
});
|
|
4226
|
+
var FreeformFollowUpTask = Type$2.Object({
|
|
4227
|
+
title: Type$2.String({ minLength: 1 }),
|
|
4228
|
+
brief: Type$2.String({ minLength: 1 }),
|
|
4229
|
+
suggestedTaskType: Type$2.Optional(Type$2.String({ minLength: 1 }))
|
|
4230
|
+
}, {
|
|
4231
|
+
$id: "FreeformFollowUpTask",
|
|
4232
|
+
additionalProperties: false
|
|
4233
|
+
});
|
|
4234
|
+
var FreeformOutput = Type$2.Object({
|
|
4235
|
+
summary: Type$2.String({ minLength: 1 }),
|
|
4236
|
+
artifacts: Type$2.Optional(Type$2.Array(FreeformArtifact, { maxItems: 20 })),
|
|
4237
|
+
proposedTaskType: Type$2.Optional(FreeformTaskTypeProposal),
|
|
4238
|
+
followUpTasks: Type$2.Optional(Type$2.Array(FreeformFollowUpTask, { maxItems: 20 })),
|
|
4239
|
+
diaryEntryIds: Type$2.Optional(Type$2.Array(Type$2.String({ format: "uuid" }))),
|
|
4240
|
+
verification: Type$2.Optional(VerificationRecord)
|
|
4241
|
+
}, {
|
|
4242
|
+
$id: "FreeformOutput",
|
|
4243
|
+
additionalProperties: false
|
|
4244
|
+
});
|
|
4245
|
+
//#endregion
|
|
4192
4246
|
//#region ../../libs/tasks/src/task-types/fulfill-brief.ts
|
|
4193
4247
|
/**
|
|
4194
4248
|
* `fulfill_brief` — produce a signed change against a coding brief.
|
|
@@ -4436,11 +4490,11 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
|
|
|
4436
4490
|
field: "targetTaskId",
|
|
4437
4491
|
message: `targetTaskId=${inp.targetTaskId} is a ${target.taskType}, not a run_eval`
|
|
4438
4492
|
});
|
|
4439
|
-
if (target.status !== "completed" || target.acceptedAttemptN === null) errors.push({
|
|
4493
|
+
if (!ctx.deferReadinessChecks && (target.status !== "completed" || target.acceptedAttemptN === null)) errors.push({
|
|
4440
4494
|
field: "targetTaskId",
|
|
4441
4495
|
message: `targetTaskId=${inp.targetTaskId} is not completed with an accepted attempt (status=${target.status}, acceptedAttemptN=${target.acceptedAttemptN})`
|
|
4442
4496
|
});
|
|
4443
|
-
else if (target.acceptedAttemptN !== inp.targetAttemptN) errors.push({
|
|
4497
|
+
else if (target.acceptedAttemptN !== null && target.acceptedAttemptN !== inp.targetAttemptN) errors.push({
|
|
4444
4498
|
field: "targetAttemptN",
|
|
4445
4499
|
message: `targetAttemptN=${inp.targetAttemptN} does not match the producer's acceptedAttemptN=${target.acceptedAttemptN}`
|
|
4446
4500
|
});
|
|
@@ -4451,6 +4505,7 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
|
|
|
4451
4505
|
if (errors.length > 0 || !target.correlationId) return errors;
|
|
4452
4506
|
const rubric = inp.successCriteria.rubric;
|
|
4453
4507
|
const duplicate = (await ctx.listTasksByCorrelation(target.correlationId)).find((task) => {
|
|
4508
|
+
if (task.id === ctx.currentTaskId) return false;
|
|
4454
4509
|
if (task.taskType !== "judge_eval_attempt") return false;
|
|
4455
4510
|
if (task.status === "failed" || task.status === "cancelled" || task.status === "expired") return false;
|
|
4456
4511
|
const existing = task.input;
|
|
@@ -4746,6 +4801,16 @@ function requireVerificationWhenCriteriaPresent(output, input) {
|
|
|
4746
4801
|
* / claiming a task.
|
|
4747
4802
|
*/
|
|
4748
4803
|
var BUILT_IN_TASK_TYPES = {
|
|
4804
|
+
[FREEFORM_TYPE]: {
|
|
4805
|
+
name: FREEFORM_TYPE,
|
|
4806
|
+
inputSchema: FreeformInput,
|
|
4807
|
+
outputSchema: FreeformOutput,
|
|
4808
|
+
outputKind: "artifact",
|
|
4809
|
+
workspaceScope: "attempt",
|
|
4810
|
+
sessionScope: "none",
|
|
4811
|
+
requiresReferences: false,
|
|
4812
|
+
validateOutput: requireVerificationWhenCriteriaPresent
|
|
4813
|
+
},
|
|
4749
4814
|
[FULFILL_BRIEF_TYPE]: {
|
|
4750
4815
|
name: FULFILL_BRIEF_TYPE,
|
|
4751
4816
|
inputSchema: FulfillBriefInput,
|
|
@@ -5814,11 +5879,12 @@ function getTaskExecutionPolicy(taskType) {
|
|
|
5814
5879
|
*
|
|
5815
5880
|
* Identity rule:
|
|
5816
5881
|
* - claim/execute/sign → agent-only (`task_attempts.claimed_by_agent_id`)
|
|
5817
|
-
* -
|
|
5882
|
+
* - propose/cancel → agent XOR human (dual nullable FK + XOR check)
|
|
5818
5883
|
*
|
|
5819
5884
|
* See GH issue #852 for the full design snapshot.
|
|
5820
5885
|
*/
|
|
5821
5886
|
var TaskStatus = Type$2.Union([
|
|
5887
|
+
Type$2.Literal("waiting"),
|
|
5822
5888
|
Type$2.Literal("queued"),
|
|
5823
5889
|
Type$2.Literal("dispatched"),
|
|
5824
5890
|
Type$2.Literal("running"),
|
|
@@ -5861,6 +5927,36 @@ var TaskMessageKind = Type$2.Union([
|
|
|
5861
5927
|
var Uuid = Type$2.String({ format: "uuid" });
|
|
5862
5928
|
var Cid = Type$2.String({ minLength: 1 });
|
|
5863
5929
|
var IsoTimestamp = Type$2.String({ format: "date-time" });
|
|
5930
|
+
var MAX_CLAIM_CONDITION_BRANCHES = 8;
|
|
5931
|
+
var MAX_CLAIM_CONDITION_STATUSES = 8;
|
|
5932
|
+
var ClaimCondition = Type$2.Recursive((Self) => Type$2.Union([
|
|
5933
|
+
Type$2.Object({
|
|
5934
|
+
op: Type$2.Literal("all"),
|
|
5935
|
+
conditions: Type$2.Array(Self, {
|
|
5936
|
+
minItems: 1,
|
|
5937
|
+
maxItems: MAX_CLAIM_CONDITION_BRANCHES
|
|
5938
|
+
})
|
|
5939
|
+
}, { additionalProperties: false }),
|
|
5940
|
+
Type$2.Object({
|
|
5941
|
+
op: Type$2.Literal("any"),
|
|
5942
|
+
conditions: Type$2.Array(Self, {
|
|
5943
|
+
minItems: 1,
|
|
5944
|
+
maxItems: MAX_CLAIM_CONDITION_BRANCHES
|
|
5945
|
+
})
|
|
5946
|
+
}, { additionalProperties: false }),
|
|
5947
|
+
Type$2.Object({
|
|
5948
|
+
op: Type$2.Literal("task_status"),
|
|
5949
|
+
taskId: Uuid,
|
|
5950
|
+
statuses: Type$2.Array(Type$2.Ref(TaskStatus), {
|
|
5951
|
+
minItems: 1,
|
|
5952
|
+
maxItems: MAX_CLAIM_CONDITION_STATUSES
|
|
5953
|
+
})
|
|
5954
|
+
}, { additionalProperties: false }),
|
|
5955
|
+
Type$2.Object({
|
|
5956
|
+
op: Type$2.Literal("task_accepted"),
|
|
5957
|
+
taskId: Uuid
|
|
5958
|
+
}, { additionalProperties: false })
|
|
5959
|
+
]), { $id: "ClaimCondition" });
|
|
5864
5960
|
/**
|
|
5865
5961
|
* Reference to another task's output or an external artifact.
|
|
5866
5962
|
* Embedded in `tasks.references` JSONB array.
|
|
@@ -5937,9 +6033,10 @@ Type$2.Object({
|
|
|
5937
6033
|
inputCid: Cid,
|
|
5938
6034
|
references: Type$2.Array(TaskRef),
|
|
5939
6035
|
correlationId: Type$2.Union([Uuid, Type$2.Null()]),
|
|
5940
|
-
|
|
5941
|
-
|
|
6036
|
+
proposedByAgentId: Type$2.Union([Uuid, Type$2.Null()]),
|
|
6037
|
+
proposedByHumanId: Type$2.Union([Uuid, Type$2.Null()]),
|
|
5942
6038
|
acceptedAttemptN: Type$2.Union([Type$2.Number(), Type$2.Null()]),
|
|
6039
|
+
claimCondition: Type$2.Union([ClaimCondition, Type$2.Null()]),
|
|
5943
6040
|
requiredExecutorTrustLevel: ExecutorTrustLevel,
|
|
5944
6041
|
allowedExecutors: Type$2.Array(ExecutorRef, { maxItems: 16 }),
|
|
5945
6042
|
status: TaskStatus,
|
|
@@ -6199,7 +6296,7 @@ var PROMPT_SEPARATOR = "\n\n---\n\n";
|
|
|
6199
6296
|
* declared order, same separator.
|
|
6200
6297
|
*
|
|
6201
6298
|
* No fetching, no hashing — bytes are inlined in `ContextRef.content`,
|
|
6202
|
-
* and the task's `inputCid` already pins the entire input. The
|
|
6299
|
+
* and the task's `inputCid` already pins the entire input. The proposer
|
|
6203
6300
|
* chose these bytes; the resolver just dispatches them.
|
|
6204
6301
|
*
|
|
6205
6302
|
* The function is pure with respect to its arguments: file writes are
|
|
@@ -6622,7 +6719,7 @@ function buildCuratePackUserPrompt(input, ctx) {
|
|
|
6622
6719
|
"to defend it in the summary."
|
|
6623
6720
|
].join("\n");
|
|
6624
6721
|
const constraintsLines = [];
|
|
6625
|
-
if (entryTypesPinned) constraintsLines.push(`- Entry types pinned by
|
|
6722
|
+
if (entryTypesPinned) constraintsLines.push(`- Entry types pinned by proposer (do not widen): ${entryTypes.map((t) => `\`${t}\``).join(", ")}`);
|
|
6626
6723
|
else constraintsLines.push("- Entry types: **you choose**. The diary contains three kinds:", " - `episodic` — incident reports, \"what happened and how we fixed it\" narratives.", " - `semantic` — durable decisions, patterns, design rationale.", " - `procedural` — commit audit trails / changelog-style provenance.", " Pick the subset that fits the prompt. For \"failures and workarounds\"", " or \"decisions we made\" you generally do NOT want `procedural` — those", " entries are append-only commit logs and produce changelog-shaped packs.", " Include `procedural` only when the prompt explicitly asks for changelog-", " style content (e.g., \"what shipped this week\"). State your choice", " briefly in the final `summary`.");
|
|
6627
6724
|
constraintsLines.push(`- Recipe tag: \`${resolvedRecipe}\` (recorded on pack params)`);
|
|
6628
6725
|
constraintsLines.push(tokenBudget ? `- Token budget (soft cap on final pack): ${tokenBudget}. Pick entry count so the pack fits — estimate ~300 tok/entry as a starting heuristic, tighten after inspecting actual content lengths.` : "- No token budget — size the pack to match the prompt, not an arbitrary target.");
|
|
@@ -6768,6 +6865,97 @@ function buildCuratePackUserPrompt(input, ctx) {
|
|
|
6768
6865
|
]);
|
|
6769
6866
|
}
|
|
6770
6867
|
//#endregion
|
|
6868
|
+
//#region ../../libs/agent-runtime/src/prompts/freeform.ts
|
|
6869
|
+
function buildFreeformUserPrompt(input, ctx) {
|
|
6870
|
+
const header = [
|
|
6871
|
+
"# Freeform Task Agent",
|
|
6872
|
+
"",
|
|
6873
|
+
"You are handling an exploratory MoltNet task that does not yet have a",
|
|
6874
|
+
"more specific execution contract. Treat the brief as the source of truth,",
|
|
6875
|
+
"use judgment, and keep the result useful enough for a human or another",
|
|
6876
|
+
"agent to continue from it.",
|
|
6877
|
+
"",
|
|
6878
|
+
`Task id: \`${ctx.taskId}\``
|
|
6879
|
+
].join("\n");
|
|
6880
|
+
const expectedOutput = input.expectedOutput ?? "";
|
|
6881
|
+
const constraints = input.constraints?.length ? input.constraints.map((constraint) => `- ${constraint}`).join("\n") : "";
|
|
6882
|
+
const suggestedTaskType = input.suggestedTaskType ? [`The proposer suggested task type \`${input.suggestedTaskType}\`.`, "Use it as a hint, not as a contract."].join("\n") : "";
|
|
6883
|
+
const workflow = [
|
|
6884
|
+
"1. Clarify the real objective from the brief before acting.",
|
|
6885
|
+
"2. Gather enough context to avoid guessing.",
|
|
6886
|
+
"3. Complete the requested work when it is safe and bounded.",
|
|
6887
|
+
"4. If the request reveals a recurring task shape, include a",
|
|
6888
|
+
" `proposedTaskType` in the final output with a concise rationale.",
|
|
6889
|
+
"5. If the work should be split or continued, include `followUpTasks`."
|
|
6890
|
+
].join("\n");
|
|
6891
|
+
return assembleTaskPrompt("freeform", [
|
|
6892
|
+
{
|
|
6893
|
+
id: "freeform.header",
|
|
6894
|
+
source: "header",
|
|
6895
|
+
body: header
|
|
6896
|
+
},
|
|
6897
|
+
{
|
|
6898
|
+
id: "freeform.title",
|
|
6899
|
+
source: "task_input",
|
|
6900
|
+
header: "Title",
|
|
6901
|
+
body: input.title ?? ""
|
|
6902
|
+
},
|
|
6903
|
+
{
|
|
6904
|
+
id: "freeform.brief",
|
|
6905
|
+
source: "task_input",
|
|
6906
|
+
header: "Brief",
|
|
6907
|
+
body: input.brief
|
|
6908
|
+
},
|
|
6909
|
+
{
|
|
6910
|
+
id: "freeform.expected_output",
|
|
6911
|
+
source: "task_input",
|
|
6912
|
+
header: "Expected Output",
|
|
6913
|
+
body: expectedOutput
|
|
6914
|
+
},
|
|
6915
|
+
{
|
|
6916
|
+
id: "freeform.constraints",
|
|
6917
|
+
source: "task_input",
|
|
6918
|
+
header: "Constraints",
|
|
6919
|
+
body: constraints
|
|
6920
|
+
},
|
|
6921
|
+
{
|
|
6922
|
+
id: "freeform.suggested_task_type",
|
|
6923
|
+
source: "task_input",
|
|
6924
|
+
header: "Suggested Task Type",
|
|
6925
|
+
body: suggestedTaskType
|
|
6926
|
+
},
|
|
6927
|
+
{
|
|
6928
|
+
id: "freeform.workflow",
|
|
6929
|
+
source: "static",
|
|
6930
|
+
header: "Workflow",
|
|
6931
|
+
body: workflow
|
|
6932
|
+
},
|
|
6933
|
+
{
|
|
6934
|
+
id: "freeform.verification",
|
|
6935
|
+
source: "verification",
|
|
6936
|
+
body: buildSelfVerificationBlock(ctx.taskId)
|
|
6937
|
+
},
|
|
6938
|
+
{
|
|
6939
|
+
id: "freeform.final_output",
|
|
6940
|
+
source: "final_output",
|
|
6941
|
+
body: buildFinalOutputBlock({
|
|
6942
|
+
taskType: "freeform",
|
|
6943
|
+
outputSchemaName: "FreeformOutput",
|
|
6944
|
+
shapeSketch: [
|
|
6945
|
+
"{",
|
|
6946
|
+
" \"summary\": \"<2-5 sentence result>\",",
|
|
6947
|
+
" \"artifacts\": [{ \"kind\": \"...\", \"title\": \"...\", \"description\": \"...\", \"url\": \"...\", \"path\": \"...\" }],",
|
|
6948
|
+
" \"proposedTaskType\": { \"name\": \"...\", \"rationale\": \"...\", \"inputShape\": {}, \"outputShape\": {} },",
|
|
6949
|
+
" \"followUpTasks\": [{ \"title\": \"...\", \"brief\": \"...\", \"suggestedTaskType\": \"...\" }],",
|
|
6950
|
+
" \"diaryEntryIds\": [\"...\"],",
|
|
6951
|
+
" \"verification\": <required iff input.successCriteria; see Self-verification>",
|
|
6952
|
+
"}"
|
|
6953
|
+
].join("\n")
|
|
6954
|
+
})
|
|
6955
|
+
}
|
|
6956
|
+
]);
|
|
6957
|
+
}
|
|
6958
|
+
//#endregion
|
|
6771
6959
|
//#region ../../libs/agent-runtime/src/prompts/fulfill-brief.ts
|
|
6772
6960
|
/**
|
|
6773
6961
|
* Build the first user-message prompt for a `fulfill_brief` task.
|
|
@@ -6817,7 +7005,11 @@ function buildFulfillBriefUserPrompt(input, ctx) {
|
|
|
6817
7005
|
"5. For every commit, create a signed diary entry first via",
|
|
6818
7006
|
" `moltnet_create_entry` and embed its id in the commit trailer",
|
|
6819
7007
|
" `MoltNet-Diary: <id>` (per the runtime instructor).",
|
|
6820
|
-
"6. Push the branch and open a PR
|
|
7008
|
+
"6. Push the branch and open a PR — run `git push` and `gh pr create`",
|
|
7009
|
+
" IN the VM with your normal `bash` tool (use the",
|
|
7010
|
+
" `GH_TOKEN=$(moltnet github token …) gh …` form from the runtime",
|
|
7011
|
+
" instructor). Do NOT use `moltnet_host_exec` for this; it needs human",
|
|
7012
|
+
" approval that is unavailable in a headless run."
|
|
6821
7013
|
].join("\n");
|
|
6822
7014
|
return assembleTaskPrompt("fulfill_brief", [
|
|
6823
7015
|
{
|
|
@@ -7517,6 +7709,12 @@ function buildRunEvalUserPrompt(input, ctx) {
|
|
|
7517
7709
|
*/
|
|
7518
7710
|
function buildTaskUserPrompt(task, ctx) {
|
|
7519
7711
|
switch (task.taskType) {
|
|
7712
|
+
case FREEFORM_TYPE:
|
|
7713
|
+
if (!Check(FreeformInput, task.input)) {
|
|
7714
|
+
const errors = [...Errors(FreeformInput, task.input)];
|
|
7715
|
+
throw new Error(`freeform input failed validation: ${JSON.stringify(errors.slice(0, 3))}`);
|
|
7716
|
+
}
|
|
7717
|
+
return buildFreeformUserPrompt(task.input, { taskId: ctx.taskId });
|
|
7520
7718
|
case FULFILL_BRIEF_TYPE:
|
|
7521
7719
|
if (!Check(FulfillBriefInput, task.input)) {
|
|
7522
7720
|
const errors = [...Errors(FulfillBriefInput, task.input)];
|
|
@@ -7858,7 +8056,7 @@ var ApiTaskReporter = class {
|
|
|
7858
8056
|
const response = await this.opts.tasks.heartbeat(this.taskId, this.attemptN, body);
|
|
7859
8057
|
if (response?.cancelled && !this.cancelController.signal.aborted) {
|
|
7860
8058
|
this.observedCancelReason = response.cancelReason ?? null;
|
|
7861
|
-
this.cancelController.abort(/* @__PURE__ */ new Error(`Task cancelled by
|
|
8059
|
+
this.cancelController.abort(/* @__PURE__ */ new Error(`Task cancelled by proposer${this.observedCancelReason ? `: ${this.observedCancelReason}` : ""}`));
|
|
7862
8060
|
if (this.heartbeatTimer) {
|
|
7863
8061
|
clearInterval(this.heartbeatTimer);
|
|
7864
8062
|
this.heartbeatTimer = null;
|
|
@@ -7935,7 +8133,7 @@ var AgentRuntime = class {
|
|
|
7935
8133
|
outputCid: null,
|
|
7936
8134
|
error: {
|
|
7937
8135
|
code: "task_cancelled",
|
|
7938
|
-
message: reporter.cancelReason ?? "Task cancelled by
|
|
8136
|
+
message: reporter.cancelReason ?? "Task cancelled by proposer while executor was running.",
|
|
7939
8137
|
retryable: false
|
|
7940
8138
|
}
|
|
7941
8139
|
};
|
|
@@ -9081,84 +9279,6 @@ var searchDiary = (options) => (options?.client ?? client).post({
|
|
|
9081
9279
|
}
|
|
9082
9280
|
});
|
|
9083
9281
|
/**
|
|
9084
|
-
* Get a digest of recent diary entries.
|
|
9085
|
-
*/
|
|
9086
|
-
var reflectDiary = (options) => (options.client ?? client).get({
|
|
9087
|
-
security: [
|
|
9088
|
-
{
|
|
9089
|
-
scheme: "bearer",
|
|
9090
|
-
type: "http"
|
|
9091
|
-
},
|
|
9092
|
-
{
|
|
9093
|
-
name: "X-Moltnet-Session-Token",
|
|
9094
|
-
type: "apiKey"
|
|
9095
|
-
},
|
|
9096
|
-
{
|
|
9097
|
-
in: "cookie",
|
|
9098
|
-
name: "ory_kratos_session",
|
|
9099
|
-
type: "apiKey"
|
|
9100
|
-
}
|
|
9101
|
-
],
|
|
9102
|
-
url: "/diaries/reflect",
|
|
9103
|
-
...options
|
|
9104
|
-
});
|
|
9105
|
-
/**
|
|
9106
|
-
* [DEPRECATED] Server-side consolidation is obsolete. Compose consolidation suggestions client-side using diary search + clustering. Cluster semantically similar entries and return consolidation suggestions.
|
|
9107
|
-
*
|
|
9108
|
-
* @deprecated
|
|
9109
|
-
*/
|
|
9110
|
-
var consolidateDiary = (options) => (options.client ?? client).post({
|
|
9111
|
-
security: [
|
|
9112
|
-
{
|
|
9113
|
-
scheme: "bearer",
|
|
9114
|
-
type: "http"
|
|
9115
|
-
},
|
|
9116
|
-
{
|
|
9117
|
-
name: "X-Moltnet-Session-Token",
|
|
9118
|
-
type: "apiKey"
|
|
9119
|
-
},
|
|
9120
|
-
{
|
|
9121
|
-
in: "cookie",
|
|
9122
|
-
name: "ory_kratos_session",
|
|
9123
|
-
type: "apiKey"
|
|
9124
|
-
}
|
|
9125
|
-
],
|
|
9126
|
-
url: "/diaries/{id}/consolidate",
|
|
9127
|
-
...options,
|
|
9128
|
-
headers: {
|
|
9129
|
-
"Content-Type": "application/json",
|
|
9130
|
-
...options.headers
|
|
9131
|
-
}
|
|
9132
|
-
});
|
|
9133
|
-
/**
|
|
9134
|
-
* [DEPRECATED] Server-side compilation is obsolete. Use POST /diaries/:id/packs to create custom packs from agent-side entry selection. Compile a token-budget-fitted context pack from diary entries.
|
|
9135
|
-
*
|
|
9136
|
-
* @deprecated
|
|
9137
|
-
*/
|
|
9138
|
-
var compileDiary = (options) => (options.client ?? client).post({
|
|
9139
|
-
security: [
|
|
9140
|
-
{
|
|
9141
|
-
scheme: "bearer",
|
|
9142
|
-
type: "http"
|
|
9143
|
-
},
|
|
9144
|
-
{
|
|
9145
|
-
name: "X-Moltnet-Session-Token",
|
|
9146
|
-
type: "apiKey"
|
|
9147
|
-
},
|
|
9148
|
-
{
|
|
9149
|
-
in: "cookie",
|
|
9150
|
-
name: "ory_kratos_session",
|
|
9151
|
-
type: "apiKey"
|
|
9152
|
-
}
|
|
9153
|
-
],
|
|
9154
|
-
url: "/diaries/{id}/compile",
|
|
9155
|
-
...options,
|
|
9156
|
-
headers: {
|
|
9157
|
-
"Content-Type": "application/json",
|
|
9158
|
-
...options.headers
|
|
9159
|
-
}
|
|
9160
|
-
});
|
|
9161
|
-
/**
|
|
9162
9282
|
* Export the provenance graph for a persisted context pack by ID.
|
|
9163
9283
|
*/
|
|
9164
9284
|
var getContextPackProvenanceById = (options) => (options.client ?? client).get({
|
|
@@ -10458,7 +10578,7 @@ var MoltNetError = class extends Error {
|
|
|
10458
10578
|
/**
|
|
10459
10579
|
* Populated when the server returned a `VALIDATION_FAILED` problem
|
|
10460
10580
|
* (status 400) with field-level errors. Empty / undefined for every
|
|
10461
|
-
* other problem kind.
|
|
10581
|
+
* other problem kind. Proposer scripts surface these to operators so
|
|
10462
10582
|
* they don't have to re-run with curl to see what was rejected.
|
|
10463
10583
|
*/
|
|
10464
10584
|
validationErrors;
|
|
@@ -10634,22 +10754,6 @@ function createDiariesNamespace(context) {
|
|
|
10634
10754
|
path: { id }
|
|
10635
10755
|
}));
|
|
10636
10756
|
},
|
|
10637
|
-
async consolidate(id, body) {
|
|
10638
|
-
return unwrapResult(await consolidateDiary({
|
|
10639
|
-
client,
|
|
10640
|
-
auth,
|
|
10641
|
-
path: { id },
|
|
10642
|
-
body
|
|
10643
|
-
}));
|
|
10644
|
-
},
|
|
10645
|
-
async compile(id, body) {
|
|
10646
|
-
return unwrapResult(await compileDiary({
|
|
10647
|
-
client,
|
|
10648
|
-
auth,
|
|
10649
|
-
path: { id },
|
|
10650
|
-
body
|
|
10651
|
-
}));
|
|
10652
|
-
},
|
|
10653
10757
|
async tags(diaryId, query) {
|
|
10654
10758
|
return unwrapResult(await listDiaryTags({
|
|
10655
10759
|
client,
|
|
@@ -11721,13 +11825,6 @@ function createEntriesNamespace(context) {
|
|
|
11721
11825
|
body
|
|
11722
11826
|
}));
|
|
11723
11827
|
},
|
|
11724
|
-
async reflect(query) {
|
|
11725
|
-
return unwrapResult(await reflectDiary({
|
|
11726
|
-
client,
|
|
11727
|
-
auth,
|
|
11728
|
-
query
|
|
11729
|
-
}));
|
|
11730
|
-
},
|
|
11731
11828
|
async verify(entryId) {
|
|
11732
11829
|
return unwrapResult(await verifyDiaryEntryById({
|
|
11733
11830
|
client,
|
|
@@ -15169,7 +15266,7 @@ function createMoltNetTools(config) {
|
|
|
15169
15266
|
defineTool({
|
|
15170
15267
|
name: "moltnet_host_exec",
|
|
15171
15268
|
label: "Run command on host (escape hatch — requires user approval)",
|
|
15172
|
-
description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog — do NOT call this tool speculatively.
|
|
15269
|
+
description: "Runs a command on the HOST machine, outside the sandbox VM. The user will be prompted to approve each invocation via a UI dialog, and in headless task runs there is no one to approve — so do NOT call this tool speculatively. Routine git and gh work — pushing branches, opening pull requests, etc. — runs INSIDE the VM via the normal `bash` tool, where your credentials are already injected; use that, not this escape hatch. Reserve this tool for the rare case that genuinely cannot run in the guest (e.g. reaching a host-only resource the VM has no path to).\n\nAllowed executables: git, gh, moltnet. Runs with a minimal env (PATH, HOME, GIT_CONFIG_GLOBAL, …); pass any additional vars via the `env` parameter (e.g. GH_TOKEN). Every invocation is logged as an auditable host execution.",
|
|
15173
15270
|
parameters: Type.Object({
|
|
15174
15271
|
executable: Type.String({ description: "Executable to run (git | gh | moltnet)" }),
|
|
15175
15272
|
args: Type.Array(Type.String(), { description: "Arguments to pass to the executable" }),
|
|
@@ -16243,6 +16340,11 @@ function buildRuntimeInstructor(ctx) {
|
|
|
16243
16340
|
"",
|
|
16244
16341
|
"- `git push` uses the gitconfig-configured credential helper and is not",
|
|
16245
16342
|
" a `gh` call — it does not need `GH_TOKEN`.",
|
|
16343
|
+
"- Run `git` and `gh` in the VM with your normal `bash` tool — your",
|
|
16344
|
+
" credentials are injected here, so they work in the guest. The",
|
|
16345
|
+
" `moltnet_host_exec` tool is a last-resort host escape-hatch that",
|
|
16346
|
+
" requires human approval and is unavailable in headless task runs;",
|
|
16347
|
+
" never use it for routine git/gh.",
|
|
16246
16348
|
"",
|
|
16247
16349
|
"## Diary discipline",
|
|
16248
16350
|
"",
|
|
@@ -17418,7 +17520,7 @@ async function executePiTask(claimedTask, reporter, opts) {
|
|
|
17418
17520
|
durationMs: Date.now() - startTime,
|
|
17419
17521
|
error: {
|
|
17420
17522
|
code: "task_cancelled",
|
|
17421
|
-
message: reporter.cancelReason ?? "Task cancelled by
|
|
17523
|
+
message: reporter.cancelReason ?? "Task cancelled by proposer while pi session was running.",
|
|
17422
17524
|
retryable: false
|
|
17423
17525
|
}
|
|
17424
17526
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
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,18 +33,18 @@
|
|
|
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.19.1",
|
|
37
|
+
"@themoltnet/pi-extension": "0.20.1",
|
|
38
|
+
"@themoltnet/sdk": "0.106.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"tsx": "^4.7.0",
|
|
42
42
|
"typescript": "~5.9.2",
|
|
43
43
|
"vite": "^8.0.0",
|
|
44
44
|
"vitest": "^3.0.0",
|
|
45
|
+
"@moltnet/bootstrap": "0.1.0",
|
|
45
46
|
"@moltnet/crypto-service": "0.1.0",
|
|
46
47
|
"@moltnet/database": "0.1.0",
|
|
47
|
-
"@moltnet/bootstrap": "0.1.0",
|
|
48
48
|
"@moltnet/tasks": "0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"nx": {
|