@themoltnet/agent-daemon 0.24.0 → 0.25.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 -8
- package/dist/main.js +207 -13
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -179,14 +179,13 @@ This matters for evals in particular. `run_eval` tasks declare their intended
|
|
|
179
179
|
workspace shape in `input.execution.workspace`: `none` becomes a
|
|
180
180
|
`scratch_mount`, `shared_mount` uses the daemon mount, and
|
|
181
181
|
`dedicated_worktree` uses an isolated checkout. Downstream
|
|
182
|
-
`judge_eval_attempt` tasks
|
|
183
|
-
session
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
producer
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
with `when.workspaceMode`.
|
|
182
|
+
`judge_eval_attempt` tasks can hydrate the producer Pi session from durable
|
|
183
|
+
runtime-session storage when producer slot/workspace metadata is available but
|
|
184
|
+
the local session file is unavailable. Workspace copying still depends on
|
|
185
|
+
producer slot/workspace metadata; if the daemon cannot resolve the required
|
|
186
|
+
producer context, the judge fails with `producer_context_missing`.
|
|
187
|
+
Repo-specific `resumeCommands` that should not run in scratch mode must still
|
|
188
|
+
be guarded with `when.workspaceMode`.
|
|
190
189
|
|
|
191
190
|
### 1. Start the local stack
|
|
192
191
|
|
package/dist/main.js
CHANGED
|
@@ -12,7 +12,7 @@ import { parseArgs, promisify } from "node:util";
|
|
|
12
12
|
import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource } from "@themoltnet/agent-runtime";
|
|
13
13
|
import { createPiTaskExecutor, findMainWorktree } from "@themoltnet/pi-extension";
|
|
14
14
|
import { execFile, execFileSync } from "node:child_process";
|
|
15
|
-
import { accessSync, constants, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
15
|
+
import { accessSync, constants, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
16
16
|
import { MoltNetError, connect } from "@themoltnet/sdk";
|
|
17
17
|
import { once } from "node:events";
|
|
18
18
|
import { pino, transport } from "pino";
|
|
@@ -22,6 +22,8 @@ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
|
22
22
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
23
23
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
24
24
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
25
|
+
import { mkdir } from "node:fs/promises";
|
|
26
|
+
import { pipeline } from "node:stream/promises";
|
|
25
27
|
//#region ../../libs/observability/src/instrumentation.ts
|
|
26
28
|
/**
|
|
27
29
|
* Register OTel auto-instrumentation for common Node.js modules.
|
|
@@ -4569,6 +4571,64 @@ _Object_({
|
|
|
4569
4571
|
additionalProperties: false
|
|
4570
4572
|
});
|
|
4571
4573
|
//#endregion
|
|
4574
|
+
//#region ../../libs/tasks/src/runtime-sessions.ts
|
|
4575
|
+
var RuntimeSessionKind = Union([
|
|
4576
|
+
Literal("root"),
|
|
4577
|
+
Literal("extend"),
|
|
4578
|
+
Literal("fork")
|
|
4579
|
+
]);
|
|
4580
|
+
var RuntimeSessionCheckpointKind = Union([Literal("attempt_final")]);
|
|
4581
|
+
_Object_({
|
|
4582
|
+
id: String$1({ format: "uuid" }),
|
|
4583
|
+
teamId: String$1({ format: "uuid" }),
|
|
4584
|
+
taskId: String$1({ format: "uuid" }),
|
|
4585
|
+
attemptN: Integer({ minimum: 1 }),
|
|
4586
|
+
sourceSlotId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4587
|
+
sourceRuntimeProfileId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4588
|
+
sessionKind: RuntimeSessionKind,
|
|
4589
|
+
parentSessionId: Union([String$1({ format: "uuid" }), Null()]),
|
|
4590
|
+
contentType: String$1({
|
|
4591
|
+
minLength: 1,
|
|
4592
|
+
maxLength: 200
|
|
4593
|
+
}),
|
|
4594
|
+
contentEncoding: Union([String$1({
|
|
4595
|
+
minLength: 1,
|
|
4596
|
+
maxLength: 100
|
|
4597
|
+
}), Null()]),
|
|
4598
|
+
sizeBytes: Integer({ minimum: 0 }),
|
|
4599
|
+
sha256: String$1({
|
|
4600
|
+
minLength: 64,
|
|
4601
|
+
maxLength: 64
|
|
4602
|
+
}),
|
|
4603
|
+
storageClass: String$1({
|
|
4604
|
+
minLength: 1,
|
|
4605
|
+
maxLength: 100
|
|
4606
|
+
}),
|
|
4607
|
+
checkpointKind: RuntimeSessionCheckpointKind,
|
|
4608
|
+
uploadedAt: String$1({ format: "date-time" })
|
|
4609
|
+
}, { $id: "RuntimeSession" });
|
|
4610
|
+
_Object_({
|
|
4611
|
+
sourceSlotId: Optional(String$1({ format: "uuid" })),
|
|
4612
|
+
sourceRuntimeProfileId: Optional(String$1({ format: "uuid" })),
|
|
4613
|
+
sessionKind: RuntimeSessionKind,
|
|
4614
|
+
parentSessionId: Optional(String$1({ format: "uuid" }))
|
|
4615
|
+
}, {
|
|
4616
|
+
$id: "UploadRuntimeSessionQuery",
|
|
4617
|
+
additionalProperties: false
|
|
4618
|
+
});
|
|
4619
|
+
String$1({
|
|
4620
|
+
$id: "RuntimeSessionContent",
|
|
4621
|
+
description: "Runtime session content stream.",
|
|
4622
|
+
format: "binary"
|
|
4623
|
+
});
|
|
4624
|
+
_Object_({
|
|
4625
|
+
taskId: String$1({ format: "uuid" }),
|
|
4626
|
+
attemptN: Integer({ minimum: 1 })
|
|
4627
|
+
}, {
|
|
4628
|
+
$id: "RuntimeSessionAttemptParams",
|
|
4629
|
+
additionalProperties: false
|
|
4630
|
+
});
|
|
4631
|
+
//#endregion
|
|
4572
4632
|
//#region ../../libs/tasks/src/runtime-slots.ts
|
|
4573
4633
|
var RuntimeWorkspaceKind = Union([
|
|
4574
4634
|
Literal("origin"),
|
|
@@ -9572,12 +9632,13 @@ var ProducerContextResolutionError = class extends Error {
|
|
|
9572
9632
|
};
|
|
9573
9633
|
function createExecutionPlanCache(args) {
|
|
9574
9634
|
const cache = /* @__PURE__ */ new Map();
|
|
9635
|
+
const runtimeSessionStore = args.runtimeSessionStore ?? createNullRuntimeSessionStore();
|
|
9575
9636
|
return {
|
|
9576
9637
|
async getOrCreate(claimedTask) {
|
|
9577
9638
|
const key = buildClaimedTaskKey(claimedTask);
|
|
9578
9639
|
const existing = cache.get(key);
|
|
9579
9640
|
if (existing) return existing;
|
|
9580
|
-
const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec, args.workspacePolicy), args.stateDirs, args.slotRegistry);
|
|
9641
|
+
const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec, args.workspacePolicy), args.stateDirs, args.slotRegistry, runtimeSessionStore);
|
|
9581
9642
|
assertPlanAllowedByWorkspacePolicy(plan, args.workspacePolicy);
|
|
9582
9643
|
cache.set(key, plan);
|
|
9583
9644
|
return plan;
|
|
@@ -9587,6 +9648,17 @@ function createExecutionPlanCache(args) {
|
|
|
9587
9648
|
}
|
|
9588
9649
|
};
|
|
9589
9650
|
}
|
|
9651
|
+
function createNullRuntimeSessionStore() {
|
|
9652
|
+
return {
|
|
9653
|
+
async findRuntimeSessionByTaskAttempt() {
|
|
9654
|
+
return null;
|
|
9655
|
+
},
|
|
9656
|
+
async hydrateSession() {
|
|
9657
|
+
throw new ProducerContextResolutionError("Cannot hydrate runtime session: no runtime session store configured");
|
|
9658
|
+
},
|
|
9659
|
+
async uploadAttemptFinal() {}
|
|
9660
|
+
};
|
|
9661
|
+
}
|
|
9590
9662
|
function assertPlanAllowedByWorkspacePolicy(plan, policy) {
|
|
9591
9663
|
const allowed = new Set(policy?.allowedWorkspaceModes && policy.allowedWorkspaceModes.length > 0 ? policy.allowedWorkspaceModes : [
|
|
9592
9664
|
"none",
|
|
@@ -9604,10 +9676,17 @@ function planToRuntimeProfileWorkspaceMode(plan) {
|
|
|
9604
9676
|
function buildClaimedTaskKey(task) {
|
|
9605
9677
|
return `${task.task.id}:${task.attemptN}`;
|
|
9606
9678
|
}
|
|
9607
|
-
async function resolveWarmSlot(slotRegistry, teamId, sourceTaskId, sourceAttemptN, stateDirs) {
|
|
9679
|
+
async function resolveWarmSlot(slotRegistry, runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs) {
|
|
9608
9680
|
const producerContext = await slotRegistry.findLatestSlotByTaskAttempt(teamId, sourceTaskId, sourceAttemptN);
|
|
9609
9681
|
if (!producerContext) return { kind: "missing" };
|
|
9610
|
-
const
|
|
9682
|
+
const localSessionPath = resolveProducerSessionPath(producerContext);
|
|
9683
|
+
const remoteSession = localSessionPath ? null : await runtimeSessionStore.findRuntimeSessionByTaskAttempt(teamId, sourceTaskId, sourceAttemptN);
|
|
9684
|
+
const sourceSessionPath = localSessionPath ? localSessionPath : remoteSession ? await runtimeSessionStore.hydrateSession({
|
|
9685
|
+
attemptN: sourceAttemptN,
|
|
9686
|
+
destinationDir: `${stateDirs.piSessionsDir}/remote-${sourceTaskId}-attempt-${sourceAttemptN}`,
|
|
9687
|
+
taskId: sourceTaskId,
|
|
9688
|
+
teamId
|
|
9689
|
+
}) : null;
|
|
9611
9690
|
if (!sourceSessionPath) return { kind: "no-session-path" };
|
|
9612
9691
|
return {
|
|
9613
9692
|
kind: "found",
|
|
@@ -9616,11 +9695,11 @@ async function resolveWarmSlot(slotRegistry, teamId, sourceTaskId, sourceAttempt
|
|
|
9616
9695
|
workspacePath: resolveProducerWorkspaceCopySource(producerContext, stateDirs)
|
|
9617
9696
|
};
|
|
9618
9697
|
}
|
|
9619
|
-
async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slotRegistry) {
|
|
9698
|
+
async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slotRegistry, runtimeSessionStore) {
|
|
9620
9699
|
if (claimedTask.task.taskType === "freeform") {
|
|
9621
9700
|
const continueFrom = claimedTask.task.input.continueFrom;
|
|
9622
9701
|
if (!continueFrom) return basePlan;
|
|
9623
|
-
const resolution = await resolveWarmSlot(slotRegistry, claimedTask.task.teamId, continueFrom.taskId, continueFrom.attemptN, stateDirs);
|
|
9702
|
+
const resolution = await resolveWarmSlot(slotRegistry, runtimeSessionStore, claimedTask.task.teamId, continueFrom.taskId, continueFrom.attemptN, stateDirs);
|
|
9624
9703
|
if (resolution.kind === "missing") throw new ProducerContextResolutionError(`Continuation source task ${continueFrom.taskId} attempt ${continueFrom.attemptN} has no live runtime slot on this daemon — claim affinity filter should have prevented this claim`);
|
|
9625
9704
|
if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Continuation source attempt ${continueFrom.taskId}/${continueFrom.attemptN} has no persisted Pi session path`);
|
|
9626
9705
|
const sessionDir = `${stateDirs.piSessionsDir}/continue-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
@@ -9657,7 +9736,7 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
9657
9736
|
const targetTaskId = typeof claimedTask.task.input.targetTaskId === "string" ? claimedTask.task.input.targetTaskId : null;
|
|
9658
9737
|
const targetAttemptN = typeof claimedTask.task.input.targetAttemptN === "number" ? claimedTask.task.input.targetAttemptN : null;
|
|
9659
9738
|
if (!targetTaskId || !targetAttemptN) throw new ProducerContextResolutionError("judge_eval_attempt is missing targetTaskId/targetAttemptN");
|
|
9660
|
-
const resolution = await resolveWarmSlot(slotRegistry, claimedTask.task.teamId, targetTaskId, targetAttemptN, stateDirs);
|
|
9739
|
+
const resolution = await resolveWarmSlot(slotRegistry, runtimeSessionStore, claimedTask.task.teamId, targetTaskId, targetAttemptN, stateDirs);
|
|
9661
9740
|
if (resolution.kind === "missing") throw new ProducerContextResolutionError(`No live producer runtime slot found for task ${targetTaskId} attempt ${targetAttemptN}`);
|
|
9662
9741
|
if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Producer task ${targetTaskId} attempt ${targetAttemptN} has no persisted Pi session path`);
|
|
9663
9742
|
return {
|
|
@@ -10067,6 +10146,70 @@ function isExecutable(path) {
|
|
|
10067
10146
|
}
|
|
10068
10147
|
}
|
|
10069
10148
|
//#endregion
|
|
10149
|
+
//#region src/lib/runtime-sessions.ts
|
|
10150
|
+
function resolveRuntimeSessionKind(claimedTask) {
|
|
10151
|
+
const continueFrom = resolveContinueFrom(claimedTask);
|
|
10152
|
+
if (!continueFrom) return "root";
|
|
10153
|
+
return continueFrom.mode === "fork" ? "fork" : "extend";
|
|
10154
|
+
}
|
|
10155
|
+
async function resolveParentRuntimeSession(runtimeSessionStore, claimedTask) {
|
|
10156
|
+
const continueFrom = resolveContinueFrom(claimedTask);
|
|
10157
|
+
if (!continueFrom) return null;
|
|
10158
|
+
return runtimeSessionStore.findRuntimeSessionByTaskAttempt(claimedTask.task.teamId, continueFrom.taskId, continueFrom.attemptN);
|
|
10159
|
+
}
|
|
10160
|
+
function applyRuntimeSessionUploadFailure(output, err) {
|
|
10161
|
+
if (output.status !== "completed") return output;
|
|
10162
|
+
return {
|
|
10163
|
+
...output,
|
|
10164
|
+
contentSignature: void 0,
|
|
10165
|
+
error: {
|
|
10166
|
+
code: "runtime_session_upload_failed",
|
|
10167
|
+
message: "Task completed, but durable runtime session checkpoint upload failed: " + (err instanceof Error ? err.message : String(err)),
|
|
10168
|
+
retryable: true
|
|
10169
|
+
},
|
|
10170
|
+
output: null,
|
|
10171
|
+
outputCid: null,
|
|
10172
|
+
status: "failed"
|
|
10173
|
+
};
|
|
10174
|
+
}
|
|
10175
|
+
function createApiRuntimeSessionStore(args) {
|
|
10176
|
+
const { agent } = args;
|
|
10177
|
+
return {
|
|
10178
|
+
async findRuntimeSessionByTaskAttempt(teamId, taskId, attemptN) {
|
|
10179
|
+
return agent.runtimeSessions.getForAttempt({
|
|
10180
|
+
attemptN,
|
|
10181
|
+
taskId
|
|
10182
|
+
}, { teamId });
|
|
10183
|
+
},
|
|
10184
|
+
async hydrateSession(input) {
|
|
10185
|
+
const downloaded = await agent.runtimeSessions.download({
|
|
10186
|
+
attemptN: input.attemptN,
|
|
10187
|
+
taskId: input.taskId
|
|
10188
|
+
}, { teamId: input.teamId });
|
|
10189
|
+
await mkdir(input.destinationDir, { recursive: true });
|
|
10190
|
+
const sessionPath = join(input.destinationDir, `remote-${input.taskId}-attempt-${input.attemptN}.jsonl`);
|
|
10191
|
+
await pipeline(downloaded, createWriteStream(sessionPath));
|
|
10192
|
+
return sessionPath;
|
|
10193
|
+
},
|
|
10194
|
+
async uploadAttemptFinal(input) {
|
|
10195
|
+
const sessionPath = resolveLatestPiSessionPath(input.sessionDir);
|
|
10196
|
+
if (!sessionPath) throw new Error(`Cannot upload runtime session for ${input.taskId}/${input.attemptN}: no local session file in ${input.sessionDir}`);
|
|
10197
|
+
await agent.runtimeSessions.upload({
|
|
10198
|
+
attemptN: input.attemptN,
|
|
10199
|
+
taskId: input.taskId
|
|
10200
|
+
}, createReadStream(sessionPath), {
|
|
10201
|
+
parentSessionId: input.parentSessionId ?? void 0,
|
|
10202
|
+
sessionKind: input.sessionKind,
|
|
10203
|
+
sourceRuntimeProfileId: input.sourceRuntimeProfileId ?? void 0,
|
|
10204
|
+
sourceSlotId: input.sourceSlotId ?? void 0
|
|
10205
|
+
}, { teamId: input.teamId });
|
|
10206
|
+
}
|
|
10207
|
+
};
|
|
10208
|
+
}
|
|
10209
|
+
function resolveContinueFrom(claimedTask) {
|
|
10210
|
+
return claimedTask.task.input.continueFrom;
|
|
10211
|
+
}
|
|
10212
|
+
//#endregion
|
|
10070
10213
|
//#region src/lib/runtime-slots.ts
|
|
10071
10214
|
function createApiRuntimeSlotStore(args) {
|
|
10072
10215
|
const { agent } = args;
|
|
@@ -10108,7 +10251,11 @@ function createApiRuntimeSlotStore(args) {
|
|
|
10108
10251
|
}, { teamId });
|
|
10109
10252
|
if (!resolved) return null;
|
|
10110
10253
|
return {
|
|
10111
|
-
slot: {
|
|
10254
|
+
slot: {
|
|
10255
|
+
expiresAtMs: resolved.slot.expiresAtMs,
|
|
10256
|
+
id: resolved.slot.id,
|
|
10257
|
+
runtimeProfileId: resolved.slot.runtimeProfileId
|
|
10258
|
+
},
|
|
10112
10259
|
session: resolved.slot.sessionDir ? {
|
|
10113
10260
|
sessionDir: resolved.slot.sessionDir,
|
|
10114
10261
|
sessionPath: resolved.slot.sessionPath
|
|
@@ -10263,6 +10410,7 @@ async function runPolling(opts) {
|
|
|
10263
10410
|
});
|
|
10264
10411
|
for (const profile of profiles) validateRuntimeProfilePrerequisites(profile, cfg.profilePrerequisiteEnv, cfg.profilePrerequisitePath);
|
|
10265
10412
|
const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
|
|
10413
|
+
const runtimeSessionStore = createApiRuntimeSessionStore({ agent: ctx.agent });
|
|
10266
10414
|
const runtimes = /* @__PURE__ */ new Map();
|
|
10267
10415
|
for (const profile of profiles) {
|
|
10268
10416
|
const common = parseCommonOptions(values, { runtimeDefaults: {
|
|
@@ -10292,7 +10440,8 @@ async function runPolling(opts) {
|
|
|
10292
10440
|
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
10293
10441
|
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
10294
10442
|
},
|
|
10295
|
-
slotRegistry
|
|
10443
|
+
slotRegistry,
|
|
10444
|
+
runtimeSessionStore
|
|
10296
10445
|
});
|
|
10297
10446
|
runtimes.set(profile.id, {
|
|
10298
10447
|
common,
|
|
@@ -10401,7 +10550,8 @@ async function runPolling(opts) {
|
|
|
10401
10550
|
stopWhenEmpty: opts.stopWhenEmpty,
|
|
10402
10551
|
debug: baseCommon.debug,
|
|
10403
10552
|
logger: rootLogger,
|
|
10404
|
-
slotRegistry
|
|
10553
|
+
slotRegistry,
|
|
10554
|
+
sessionRegistry: runtimeSessionStore
|
|
10405
10555
|
}),
|
|
10406
10556
|
makeReporter: (claimedTask) => {
|
|
10407
10557
|
const selected = runtimeForClaimedTask(runtimes, claimedTask);
|
|
@@ -10416,7 +10566,28 @@ async function runPolling(opts) {
|
|
|
10416
10566
|
onTaskFinished: async (output, claimedTask) => {
|
|
10417
10567
|
const selected = runtimeForClaimedTask(runtimes, claimedTask);
|
|
10418
10568
|
const resolved = await slotRegistry.findLatestSlotByTaskAttempt(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN);
|
|
10419
|
-
|
|
10569
|
+
let terminalOutput = output;
|
|
10570
|
+
if (resolved?.session?.sessionDir) try {
|
|
10571
|
+
const parentSession = await resolveParentRuntimeSession(runtimeSessionStore, claimedTask);
|
|
10572
|
+
await runtimeSessionStore.uploadAttemptFinal({
|
|
10573
|
+
attemptN: claimedTask.attemptN,
|
|
10574
|
+
parentSessionId: parentSession?.id ?? null,
|
|
10575
|
+
sessionDir: resolved.session.sessionDir,
|
|
10576
|
+
sessionKind: resolveRuntimeSessionKind(claimedTask),
|
|
10577
|
+
sourceRuntimeProfileId: resolved.slot.runtimeProfileId,
|
|
10578
|
+
sourceSlotId: resolved.slot.id,
|
|
10579
|
+
taskId: claimedTask.task.id,
|
|
10580
|
+
teamId: claimedTask.task.teamId
|
|
10581
|
+
});
|
|
10582
|
+
} catch (err) {
|
|
10583
|
+
rootLogger.error({
|
|
10584
|
+
err,
|
|
10585
|
+
taskId: claimedTask.task.id,
|
|
10586
|
+
attemptN: claimedTask.attemptN
|
|
10587
|
+
}, "agent-daemon.runtime_session_upload_failed");
|
|
10588
|
+
terminalOutput = applyRuntimeSessionUploadFailure(output, err);
|
|
10589
|
+
}
|
|
10590
|
+
return finalizeTask(ctx.agent, terminalOutput, {
|
|
10420
10591
|
task: claimedTask.task,
|
|
10421
10592
|
slot: resolved ? { expiresAtMs: resolved.slot.expiresAtMs } : null,
|
|
10422
10593
|
writeCorrelationAnchors: makePrBodyAnchorWriter({
|
|
@@ -10681,6 +10852,7 @@ async function runOnce(argv) {
|
|
|
10681
10852
|
activatePiCodingAgentDir(piAgentDir.path);
|
|
10682
10853
|
const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
|
|
10683
10854
|
const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
|
|
10855
|
+
const runtimeSessionStore = createApiRuntimeSessionStore({ agent: ctx.agent });
|
|
10684
10856
|
const slotIdentity = {
|
|
10685
10857
|
agentName: opts.agent,
|
|
10686
10858
|
runtimeProfileId: profile.id
|
|
@@ -10693,7 +10865,8 @@ async function runOnce(argv) {
|
|
|
10693
10865
|
defaultWorkspaceMode: profile.defaultWorkspaceMode,
|
|
10694
10866
|
allowedWorkspaceModes: profile.allowedWorkspaceModes
|
|
10695
10867
|
},
|
|
10696
|
-
slotRegistry
|
|
10868
|
+
slotRegistry,
|
|
10869
|
+
runtimeSessionStore
|
|
10697
10870
|
});
|
|
10698
10871
|
const otelShutdown = await initWorkerOtel({
|
|
10699
10872
|
serviceName: "moltnet.agent-daemon.once",
|
|
@@ -10853,7 +11026,28 @@ async function runOnce(argv) {
|
|
|
10853
11026
|
}),
|
|
10854
11027
|
onTaskFinished: async (output, claimedTask) => {
|
|
10855
11028
|
const resolved = await slotRegistry.findLatestSlotByTaskAttempt(claimedTask.task.teamId, claimedTask.task.id, claimedTask.attemptN);
|
|
10856
|
-
|
|
11029
|
+
let terminalOutput = output;
|
|
11030
|
+
if (resolved?.session?.sessionDir) try {
|
|
11031
|
+
const parentSession = await resolveParentRuntimeSession(runtimeSessionStore, claimedTask);
|
|
11032
|
+
await runtimeSessionStore.uploadAttemptFinal({
|
|
11033
|
+
attemptN: claimedTask.attemptN,
|
|
11034
|
+
parentSessionId: parentSession?.id ?? null,
|
|
11035
|
+
sessionDir: resolved.session.sessionDir,
|
|
11036
|
+
sessionKind: resolveRuntimeSessionKind(claimedTask),
|
|
11037
|
+
sourceRuntimeProfileId: resolved.slot.runtimeProfileId,
|
|
11038
|
+
sourceSlotId: resolved.slot.id,
|
|
11039
|
+
taskId: claimedTask.task.id,
|
|
11040
|
+
teamId: claimedTask.task.teamId
|
|
11041
|
+
});
|
|
11042
|
+
} catch (err) {
|
|
11043
|
+
rootLogger.error({
|
|
11044
|
+
err,
|
|
11045
|
+
taskId: claimedTask.task.id,
|
|
11046
|
+
attemptN: claimedTask.attemptN
|
|
11047
|
+
}, "agent-daemon.runtime_session_upload_failed");
|
|
11048
|
+
terminalOutput = applyRuntimeSessionUploadFailure(output, err);
|
|
11049
|
+
}
|
|
11050
|
+
return finalizeTask(ctx.agent, terminalOutput, {
|
|
10857
11051
|
task: claimedTask.task,
|
|
10858
11052
|
slot: resolved ? { expiresAtMs: resolved.slot.expiresAtMs } : null,
|
|
10859
11053
|
writeCorrelationAnchors,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.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.",
|
|
@@ -45,18 +45,18 @@
|
|
|
45
45
|
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
46
46
|
"pino": "^10.3.1",
|
|
47
47
|
"pino-pretty": "^13.1.3",
|
|
48
|
-
"@themoltnet/
|
|
49
|
-
"@themoltnet/
|
|
50
|
-
"@themoltnet/
|
|
48
|
+
"@themoltnet/pi-extension": "0.27.2",
|
|
49
|
+
"@themoltnet/sdk": "0.113.1",
|
|
50
|
+
"@themoltnet/agent-runtime": "0.30.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"tsx": "^4.7.0",
|
|
54
54
|
"typescript": "~5.9.2",
|
|
55
55
|
"vite": "^8.0.0",
|
|
56
56
|
"vitest": "^3.0.0",
|
|
57
|
-
"@moltnet/observability": "0.1.0",
|
|
58
57
|
"@moltnet/bootstrap": "0.1.0",
|
|
59
58
|
"@moltnet/crypto-service": "0.1.0",
|
|
59
|
+
"@moltnet/observability": "0.1.0",
|
|
60
60
|
"@moltnet/tasks": "0.1.0"
|
|
61
61
|
},
|
|
62
62
|
"nx": {
|