@themoltnet/agent-daemon 0.26.0 → 0.27.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 +7 -5
- package/dist/main.js +345 -8
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -19,17 +19,19 @@ Run commands through the published package:
|
|
|
19
19
|
|
|
20
20
|
## Modes
|
|
21
21
|
|
|
22
|
-
| Mode
|
|
23
|
-
|
|
|
24
|
-
| `once`
|
|
25
|
-
| `poll`
|
|
26
|
-
| `drain`
|
|
22
|
+
| Mode | Purpose |
|
|
23
|
+
| --------------- | ----------------------------------------------------------------------------- |
|
|
24
|
+
| `once` | Claim a single task by id and exit. Use this in CI. |
|
|
25
|
+
| `poll` | Long-running loop that claims tasks as they appear. Local/long-running hosts. |
|
|
26
|
+
| `drain` | Finalize any tasks already claimed by this agent and exit. |
|
|
27
|
+
| `sync-sessions` | Repair remote runtime-session uploads from local daemon slots. |
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
30
|
npx @themoltnet/agent-daemon once --task-id <uuid>
|
|
30
31
|
npx @themoltnet/agent-daemon poll --task-types fulfill_brief,assess_brief
|
|
31
32
|
npx @themoltnet/agent-daemon poll --task-types freeform
|
|
32
33
|
npx @themoltnet/agent-daemon drain
|
|
34
|
+
npx @themoltnet/agent-daemon sync-sessions --team <uuid> --agent <name> --dry-run
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
## Configuration
|
package/dist/main.js
CHANGED
|
@@ -7,7 +7,7 @@ import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
|
|
|
7
7
|
import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
|
|
8
8
|
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
9
9
|
import crypto from "crypto";
|
|
10
|
-
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
|
+
import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
11
11
|
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";
|
|
@@ -22,8 +22,11 @@ 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";
|
|
25
|
+
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
26
26
|
import { pipeline } from "node:stream/promises";
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
import { Writable } from "node:stream";
|
|
29
|
+
import { createGzip } from "node:zlib";
|
|
27
30
|
//#region ../../libs/observability/src/instrumentation.ts
|
|
28
31
|
/**
|
|
29
32
|
* Register OTel auto-instrumentation for common Node.js modules.
|
|
@@ -4411,6 +4414,32 @@ var RuntimeProfileAllowedWorkspaceModes = _Array_(RuntimeProfileWorkspaceMode, {
|
|
|
4411
4414
|
maxItems: 3,
|
|
4412
4415
|
uniqueItems: true
|
|
4413
4416
|
});
|
|
4417
|
+
var RuntimeProfileThinkingLevelOptions = [
|
|
4418
|
+
Literal("off"),
|
|
4419
|
+
Literal("minimal"),
|
|
4420
|
+
Literal("low"),
|
|
4421
|
+
Literal("medium"),
|
|
4422
|
+
Literal("high"),
|
|
4423
|
+
Literal("xhigh")
|
|
4424
|
+
];
|
|
4425
|
+
Union([...RuntimeProfileThinkingLevelOptions]);
|
|
4426
|
+
var RuntimeProfileNullableThinkingLevel = Union([...RuntimeProfileThinkingLevelOptions, Null()]);
|
|
4427
|
+
var RuntimeProfileNullableTemperature = Union([Null(), Number$1({
|
|
4428
|
+
minimum: 0,
|
|
4429
|
+
maximum: 2
|
|
4430
|
+
})]);
|
|
4431
|
+
var RuntimeProfileNullableTopP = Union([Null(), Number$1({
|
|
4432
|
+
minimum: 0,
|
|
4433
|
+
maximum: 1
|
|
4434
|
+
})]);
|
|
4435
|
+
var RuntimeProfileNullableTopK = Union([Integer({
|
|
4436
|
+
minimum: 1,
|
|
4437
|
+
maximum: 1e4
|
|
4438
|
+
}), Null()]);
|
|
4439
|
+
var RuntimeProfileNullableMaxOutputTokens = Union([Integer({
|
|
4440
|
+
minimum: 1,
|
|
4441
|
+
maximum: 1e6
|
|
4442
|
+
}), Null()]);
|
|
4414
4443
|
var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
|
|
4415
4444
|
Literal("shared_mount"),
|
|
4416
4445
|
Literal("dedicated_worktree"),
|
|
@@ -4535,6 +4564,11 @@ _Object_({
|
|
|
4535
4564
|
minLength: 1,
|
|
4536
4565
|
maxLength: 200
|
|
4537
4566
|
}),
|
|
4567
|
+
thinkingLevel: RuntimeProfileNullableThinkingLevel,
|
|
4568
|
+
temperature: RuntimeProfileNullableTemperature,
|
|
4569
|
+
topP: RuntimeProfileNullableTopP,
|
|
4570
|
+
topK: RuntimeProfileNullableTopK,
|
|
4571
|
+
maxOutputTokens: RuntimeProfileNullableMaxOutputTokens,
|
|
4538
4572
|
runtimeKind: Literal("gondolin_pi"),
|
|
4539
4573
|
sandbox: RuntimeProfileSandbox,
|
|
4540
4574
|
sessionStorageMode: Literal("local"),
|
|
@@ -4646,7 +4680,7 @@ var RuntimeWorkspace = _Object_({
|
|
|
4646
4680
|
createdAtMs: Integer({ minimum: 0 }),
|
|
4647
4681
|
lastUsedAtMs: Integer({ minimum: 0 })
|
|
4648
4682
|
}, { $id: "RuntimeWorkspace" });
|
|
4649
|
-
_Object_({
|
|
4683
|
+
_Object_({ items: _Array_(_Object_({
|
|
4650
4684
|
slot: _Object_({
|
|
4651
4685
|
id: String$1({ format: "uuid" }),
|
|
4652
4686
|
teamId: String$1({ format: "uuid" }),
|
|
@@ -4679,7 +4713,7 @@ _Object_({
|
|
|
4679
4713
|
expiresAtMs: Integer({ minimum: 0 })
|
|
4680
4714
|
}, { $id: "RuntimeSlot" }),
|
|
4681
4715
|
workspace: Union([RuntimeWorkspace, Null()])
|
|
4682
|
-
}, { $id: "ResolvedRuntimeSlot" });
|
|
4716
|
+
}, { $id: "ResolvedRuntimeSlot" })) }, { $id: "RuntimeSlotListResponse" });
|
|
4683
4717
|
_Object_({
|
|
4684
4718
|
agentName: String$1({
|
|
4685
4719
|
minLength: 1,
|
|
@@ -4740,6 +4774,21 @@ _Object_({
|
|
|
4740
4774
|
$id: "FindLatestRuntimeSlotForAttemptQuery",
|
|
4741
4775
|
additionalProperties: false
|
|
4742
4776
|
});
|
|
4777
|
+
_Object_({
|
|
4778
|
+
agentName: Optional(String$1({
|
|
4779
|
+
minLength: 1,
|
|
4780
|
+
maxLength: 100
|
|
4781
|
+
})),
|
|
4782
|
+
runtimeProfileId: Optional(String$1({ format: "uuid" })),
|
|
4783
|
+
state: Optional(RuntimeSlotState),
|
|
4784
|
+
limit: Optional(Integer({
|
|
4785
|
+
minimum: 1,
|
|
4786
|
+
maximum: 200
|
|
4787
|
+
}))
|
|
4788
|
+
}, {
|
|
4789
|
+
$id: "ListRuntimeSlotsQuery",
|
|
4790
|
+
additionalProperties: false
|
|
4791
|
+
});
|
|
4743
4792
|
//#endregion
|
|
4744
4793
|
//#region ../../libs/tasks/src/success-criteria.ts
|
|
4745
4794
|
/**
|
|
@@ -9235,6 +9284,8 @@ Commands:
|
|
|
9235
9284
|
once Claim and execute one specific queued task by id, then exit.
|
|
9236
9285
|
drain Poll until the queue has nothing claimable, then exit.
|
|
9237
9286
|
Useful for batch eval runs and demos.
|
|
9287
|
+
sync-sessions
|
|
9288
|
+
Repair durable runtime-session checkpoints from local slot files.
|
|
9238
9289
|
|
|
9239
9290
|
Run \`agent-daemon <command> --help\` for command-specific flags.
|
|
9240
9291
|
|
|
@@ -9325,6 +9376,34 @@ Example:
|
|
|
9325
9376
|
--task-types judge_pack \\
|
|
9326
9377
|
--agent legreffier \\
|
|
9327
9378
|
--profile eval-judge`;
|
|
9379
|
+
var SYNC_SESSIONS_HELP = `\
|
|
9380
|
+
agent-daemon sync-sessions — repair durable runtime-session checkpoints.
|
|
9381
|
+
|
|
9382
|
+
Usage:
|
|
9383
|
+
agent-daemon sync-sessions --team <uuid> --agent <name> [...]
|
|
9384
|
+
|
|
9385
|
+
Scans this daemon's team-scoped runtime slots, compares local Pi session files
|
|
9386
|
+
with durable runtime-session metadata, and uploads missing or stale checkpoints.
|
|
9387
|
+
|
|
9388
|
+
Required:
|
|
9389
|
+
--team <uuid> Team whose runtime slots to inspect.
|
|
9390
|
+
-a, --agent <name> MoltNet agent identity. Reads credentials
|
|
9391
|
+
from <agent-root>/.moltnet/<name>/moltnet.json.
|
|
9392
|
+
|
|
9393
|
+
Optional:
|
|
9394
|
+
--runtime-profile-id <uuid> Limit repair to one runtime profile.
|
|
9395
|
+
--state <active|idle> Limit scanned slots by state. Default: all.
|
|
9396
|
+
--limit <n> Max slots to scan, 1..200. Default: 100.
|
|
9397
|
+
--dry-run Report missing/stale sessions without uploading.
|
|
9398
|
+
--agent-root <path> Directory that owns .moltnet/<agent>. Default:
|
|
9399
|
+
CWD, with git root fallback when available.
|
|
9400
|
+
--debug Accepted for consistency; no extra output yet.
|
|
9401
|
+
|
|
9402
|
+
Example:
|
|
9403
|
+
agent-daemon sync-sessions \\
|
|
9404
|
+
--team 6743b4b1-6b93-46e2-a048-19490f04f91a \\
|
|
9405
|
+
--agent legreffier \\
|
|
9406
|
+
--state idle`;
|
|
9328
9407
|
function isHelpFlag(args) {
|
|
9329
9408
|
return args.includes("--help") || args.includes("-h");
|
|
9330
9409
|
}
|
|
@@ -10120,6 +10199,11 @@ async function resolveRuntimeProfile(options) {
|
|
|
10120
10199
|
teamId: profile.teamId,
|
|
10121
10200
|
provider: profile.provider,
|
|
10122
10201
|
model: profile.model,
|
|
10202
|
+
thinkingLevel: profile.thinkingLevel ?? null,
|
|
10203
|
+
temperature: profile.temperature ?? null,
|
|
10204
|
+
topP: profile.topP ?? null,
|
|
10205
|
+
topK: profile.topK ?? null,
|
|
10206
|
+
maxOutputTokens: profile.maxOutputTokens ?? null,
|
|
10123
10207
|
leaseTtlSec: profile.leaseTtlSec,
|
|
10124
10208
|
heartbeatIntervalMs: profile.heartbeatIntervalMs,
|
|
10125
10209
|
maxBatchSize: profile.maxBatchSize,
|
|
@@ -10165,7 +10249,15 @@ async function resolveProfileByName(options) {
|
|
|
10165
10249
|
const matches = (await options.agent.runtimeProfiles.list({ teamId: options.teamId })).items.filter((item) => item.name === options.profile);
|
|
10166
10250
|
if (matches.length === 0) throw new Error(`Runtime profile "${options.profile}" was not found in team ${options.teamId}.`);
|
|
10167
10251
|
if (matches.length > 1) throw new Error(`Runtime profile name "${options.profile}" is ambiguous in team ${options.teamId}. Use the profile UUID instead.`);
|
|
10168
|
-
|
|
10252
|
+
const profile = matches[0];
|
|
10253
|
+
return {
|
|
10254
|
+
...profile,
|
|
10255
|
+
thinkingLevel: profile.thinkingLevel ?? null,
|
|
10256
|
+
temperature: profile.temperature ?? null,
|
|
10257
|
+
topP: profile.topP ?? null,
|
|
10258
|
+
topK: profile.topK ?? null,
|
|
10259
|
+
maxOutputTokens: profile.maxOutputTokens ?? null
|
|
10260
|
+
};
|
|
10169
10261
|
}
|
|
10170
10262
|
function isExecutableOnPath(tool, pathValue) {
|
|
10171
10263
|
if (tool.includes("/")) return isExecutable(isAbsolute(tool) ? tool : resolve(process.cwd(), tool));
|
|
@@ -10306,6 +10398,34 @@ function createApiRuntimeSlotStore(args) {
|
|
|
10306
10398
|
} : null
|
|
10307
10399
|
};
|
|
10308
10400
|
},
|
|
10401
|
+
async listSlots(input) {
|
|
10402
|
+
const query = {
|
|
10403
|
+
agentName: input.agentName,
|
|
10404
|
+
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
10405
|
+
...input.runtimeProfileId === void 0 ? {} : { runtimeProfileId: input.runtimeProfileId },
|
|
10406
|
+
...input.state === void 0 ? {} : { state: input.state }
|
|
10407
|
+
};
|
|
10408
|
+
return (await agent.runtimeSlots.list(query, { teamId: input.teamId })).map((resolved) => ({
|
|
10409
|
+
slot: {
|
|
10410
|
+
expiresAtMs: resolved.slot.expiresAtMs,
|
|
10411
|
+
id: resolved.slot.id,
|
|
10412
|
+
lastAttemptN: resolved.slot.lastAttemptN,
|
|
10413
|
+
lastTaskId: resolved.slot.lastTaskId,
|
|
10414
|
+
runtimeProfileId: resolved.slot.runtimeProfileId,
|
|
10415
|
+
taskType: resolved.slot.taskType
|
|
10416
|
+
},
|
|
10417
|
+
session: resolved.slot.sessionDir ? {
|
|
10418
|
+
sessionDir: resolved.slot.sessionDir,
|
|
10419
|
+
sessionPath: resolved.slot.sessionPath
|
|
10420
|
+
} : null,
|
|
10421
|
+
workspace: resolved.workspace ? {
|
|
10422
|
+
kind: resolved.workspace.kind,
|
|
10423
|
+
workspaceId: resolved.workspace.workspaceId,
|
|
10424
|
+
worktreeBranch: resolved.workspace.worktreeBranch,
|
|
10425
|
+
worktreePath: resolved.workspace.worktreePath
|
|
10426
|
+
} : null
|
|
10427
|
+
}));
|
|
10428
|
+
},
|
|
10309
10429
|
async close() {}
|
|
10310
10430
|
};
|
|
10311
10431
|
}
|
|
@@ -10569,6 +10689,11 @@ async function runPolling(opts) {
|
|
|
10569
10689
|
name: profile.name,
|
|
10570
10690
|
provider: profile.provider,
|
|
10571
10691
|
model: profile.model,
|
|
10692
|
+
thinkingLevel: profile.thinkingLevel,
|
|
10693
|
+
temperature: profile.temperature,
|
|
10694
|
+
topP: profile.topP,
|
|
10695
|
+
topK: profile.topK,
|
|
10696
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
10572
10697
|
sandbox: runtime.sandbox.path,
|
|
10573
10698
|
leaseTtlSec: runtime.common.leaseTtlSec,
|
|
10574
10699
|
heartbeatIntervalMs: runtime.common.heartbeatIntervalMs,
|
|
@@ -10662,7 +10787,12 @@ async function runPolling(opts) {
|
|
|
10662
10787
|
runtimeProfileId: profile.id,
|
|
10663
10788
|
runtimeProfileName: profile.name,
|
|
10664
10789
|
provider: profile.provider,
|
|
10665
|
-
model: profile.model
|
|
10790
|
+
model: profile.model,
|
|
10791
|
+
thinkingLevel: profile.thinkingLevel,
|
|
10792
|
+
temperature: profile.temperature,
|
|
10793
|
+
topP: profile.topP,
|
|
10794
|
+
topK: profile.topK,
|
|
10795
|
+
maxOutputTokens: profile.maxOutputTokens
|
|
10666
10796
|
});
|
|
10667
10797
|
let executionPlan;
|
|
10668
10798
|
try {
|
|
@@ -10763,6 +10893,11 @@ async function runPolling(opts) {
|
|
|
10763
10893
|
mountPath: sandbox.rootDir,
|
|
10764
10894
|
provider: profile.provider,
|
|
10765
10895
|
model: profile.model,
|
|
10896
|
+
thinkingLevel: profile.thinkingLevel,
|
|
10897
|
+
temperature: profile.temperature,
|
|
10898
|
+
topP: profile.topP,
|
|
10899
|
+
topK: profile.topK,
|
|
10900
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
10766
10901
|
sandboxConfig: sandbox.config,
|
|
10767
10902
|
makeExecutionPlan: (task) => executionPlans.getOrCreate(task),
|
|
10768
10903
|
makeOnTurnEvent: makeTurnEventHandlerFactory(taskLogger),
|
|
@@ -10778,7 +10913,12 @@ async function runPolling(opts) {
|
|
|
10778
10913
|
profileId: profile.id,
|
|
10779
10914
|
profileName: profile.name,
|
|
10780
10915
|
provider: profile.provider,
|
|
10781
|
-
model: profile.model
|
|
10916
|
+
model: profile.model,
|
|
10917
|
+
thinkingLevel: profile.thinkingLevel,
|
|
10918
|
+
temperature: profile.temperature,
|
|
10919
|
+
topP: profile.topP,
|
|
10920
|
+
topK: profile.topK,
|
|
10921
|
+
maxOutputTokens: profile.maxOutputTokens
|
|
10782
10922
|
}, () => rawExecuteTask(claimedTask, reporter));
|
|
10783
10923
|
} finally {
|
|
10784
10924
|
active = null;
|
|
@@ -10935,6 +11075,11 @@ async function runOnce(argv) {
|
|
|
10935
11075
|
"moltnet.agent.name": opts.agent,
|
|
10936
11076
|
"moltnet.llm.provider": profile.provider,
|
|
10937
11077
|
"moltnet.llm.model": profile.model,
|
|
11078
|
+
...profile.thinkingLevel ? { "moltnet.llm.thinking_level": profile.thinkingLevel } : {},
|
|
11079
|
+
...profile.temperature !== null ? { "moltnet.llm.temperature": String(profile.temperature) } : {},
|
|
11080
|
+
...profile.topP !== null ? { "moltnet.llm.top_p": String(profile.topP) } : {},
|
|
11081
|
+
...profile.topK !== null ? { "moltnet.llm.top_k": String(profile.topK) } : {},
|
|
11082
|
+
...profile.maxOutputTokens !== null ? { "moltnet.llm.max_output_tokens": String(profile.maxOutputTokens) } : {},
|
|
10938
11083
|
"moltnet.runtime_profile.id": profile.id
|
|
10939
11084
|
}
|
|
10940
11085
|
});
|
|
@@ -10947,6 +11092,11 @@ async function runOnce(argv) {
|
|
|
10947
11092
|
agent: opts.agent,
|
|
10948
11093
|
provider: profile.provider,
|
|
10949
11094
|
model: profile.model,
|
|
11095
|
+
thinkingLevel: profile.thinkingLevel,
|
|
11096
|
+
temperature: profile.temperature,
|
|
11097
|
+
topP: profile.topP,
|
|
11098
|
+
topK: profile.topK,
|
|
11099
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
10950
11100
|
runtimeProfileId: profile.id,
|
|
10951
11101
|
runtimeProfileName: profile.name
|
|
10952
11102
|
});
|
|
@@ -10997,6 +11147,11 @@ async function runOnce(argv) {
|
|
|
10997
11147
|
mountPath: sandbox.rootDir,
|
|
10998
11148
|
provider: profile.provider,
|
|
10999
11149
|
model: profile.model,
|
|
11150
|
+
thinkingLevel: profile.thinkingLevel,
|
|
11151
|
+
temperature: profile.temperature,
|
|
11152
|
+
topP: profile.topP,
|
|
11153
|
+
topK: profile.topK,
|
|
11154
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
11000
11155
|
sandboxConfig: sandbox.config,
|
|
11001
11156
|
makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
|
|
11002
11157
|
onTurnEvent: makeTurnEventHandler(rootLogger, { taskId }),
|
|
@@ -11055,7 +11210,12 @@ async function runOnce(argv) {
|
|
|
11055
11210
|
profileId: profile.id,
|
|
11056
11211
|
profileName: profile.name,
|
|
11057
11212
|
provider: profile.provider,
|
|
11058
|
-
model: profile.model
|
|
11213
|
+
model: profile.model,
|
|
11214
|
+
thinkingLevel: profile.thinkingLevel,
|
|
11215
|
+
temperature: profile.temperature,
|
|
11216
|
+
topP: profile.topP,
|
|
11217
|
+
topK: profile.topK,
|
|
11218
|
+
maxOutputTokens: profile.maxOutputTokens
|
|
11059
11219
|
}, () => rawExecuteTask(claimedTask, reporter));
|
|
11060
11220
|
} finally {
|
|
11061
11221
|
activeAttemptN = null;
|
|
@@ -11145,6 +11305,182 @@ function runPoll(argv) {
|
|
|
11145
11305
|
});
|
|
11146
11306
|
}
|
|
11147
11307
|
//#endregion
|
|
11308
|
+
//#region src/lib/runtime-session-sync.ts
|
|
11309
|
+
async function syncRuntimeSessions(deps, input) {
|
|
11310
|
+
const result = {
|
|
11311
|
+
alreadyCurrent: 0,
|
|
11312
|
+
failedUpload: 0,
|
|
11313
|
+
missingLocalFile: 0,
|
|
11314
|
+
scanned: 0,
|
|
11315
|
+
unsafeSessionPath: 0,
|
|
11316
|
+
uploaded: 0,
|
|
11317
|
+
wouldUpload: 0
|
|
11318
|
+
};
|
|
11319
|
+
const slots = await deps.runtimeSlotStore.listSlots({
|
|
11320
|
+
agentName: input.agentName,
|
|
11321
|
+
limit: input.limit,
|
|
11322
|
+
runtimeProfileId: input.runtimeProfileId,
|
|
11323
|
+
state: input.state,
|
|
11324
|
+
teamId: input.teamId
|
|
11325
|
+
});
|
|
11326
|
+
for (const slot of slots) {
|
|
11327
|
+
result.scanned++;
|
|
11328
|
+
if (!slot.session?.sessionDir) {
|
|
11329
|
+
result.missingLocalFile++;
|
|
11330
|
+
continue;
|
|
11331
|
+
}
|
|
11332
|
+
if (input.sessionRootDir && !await isPathInsideRoot(slot.session.sessionDir, input.sessionRootDir)) {
|
|
11333
|
+
result.unsafeSessionPath++;
|
|
11334
|
+
continue;
|
|
11335
|
+
}
|
|
11336
|
+
const sessionPath = resolveLatestPiSessionPath(slot.session.sessionDir);
|
|
11337
|
+
if (!sessionPath || !await fileExists(sessionPath)) {
|
|
11338
|
+
result.missingLocalFile++;
|
|
11339
|
+
continue;
|
|
11340
|
+
}
|
|
11341
|
+
const remote = await deps.runtimeSessionStore.findRuntimeSessionByTaskAttempt(input.teamId, slot.slot.lastTaskId, slot.slot.lastAttemptN);
|
|
11342
|
+
if (remote) {
|
|
11343
|
+
const local = await computeCompressedSessionFingerprint(sessionPath);
|
|
11344
|
+
if (remote.sha256 === local.sha256 && remote.sizeBytes === local.bytes) {
|
|
11345
|
+
result.alreadyCurrent++;
|
|
11346
|
+
continue;
|
|
11347
|
+
}
|
|
11348
|
+
}
|
|
11349
|
+
if (input.dryRun) {
|
|
11350
|
+
result.wouldUpload++;
|
|
11351
|
+
continue;
|
|
11352
|
+
}
|
|
11353
|
+
try {
|
|
11354
|
+
const task = await deps.taskReader.get(slot.slot.lastTaskId);
|
|
11355
|
+
const claim = {
|
|
11356
|
+
attemptN: slot.slot.lastAttemptN,
|
|
11357
|
+
task: {
|
|
11358
|
+
id: task.id,
|
|
11359
|
+
input: task.input,
|
|
11360
|
+
teamId: input.teamId
|
|
11361
|
+
}
|
|
11362
|
+
};
|
|
11363
|
+
const parent = await resolveParentRuntimeSession(deps.runtimeSessionStore, claim);
|
|
11364
|
+
await deps.runtimeSessionStore.uploadAttemptFinal({
|
|
11365
|
+
attemptN: slot.slot.lastAttemptN,
|
|
11366
|
+
parentSessionId: parent?.id ?? null,
|
|
11367
|
+
sessionDir: slot.session.sessionDir,
|
|
11368
|
+
sessionKind: resolveRuntimeSessionKind(claim),
|
|
11369
|
+
sourceRuntimeProfileId: slot.slot.runtimeProfileId,
|
|
11370
|
+
sourceSlotId: slot.slot.id,
|
|
11371
|
+
taskId: slot.slot.lastTaskId,
|
|
11372
|
+
teamId: input.teamId
|
|
11373
|
+
});
|
|
11374
|
+
result.uploaded++;
|
|
11375
|
+
} catch {
|
|
11376
|
+
result.failedUpload++;
|
|
11377
|
+
}
|
|
11378
|
+
}
|
|
11379
|
+
return result;
|
|
11380
|
+
}
|
|
11381
|
+
async function fileExists(path) {
|
|
11382
|
+
try {
|
|
11383
|
+
return (await stat(path)).isFile();
|
|
11384
|
+
} catch {
|
|
11385
|
+
return false;
|
|
11386
|
+
}
|
|
11387
|
+
}
|
|
11388
|
+
async function isPathInsideRoot(path, root) {
|
|
11389
|
+
if (!isResolvedPathInsideRoot(resolve(path), resolve(root))) return false;
|
|
11390
|
+
let realResolvedPath;
|
|
11391
|
+
let realResolvedRoot;
|
|
11392
|
+
try {
|
|
11393
|
+
[realResolvedPath, realResolvedRoot] = await Promise.all([realpath(path), realpath(root)]);
|
|
11394
|
+
} catch {
|
|
11395
|
+
return true;
|
|
11396
|
+
}
|
|
11397
|
+
return isResolvedPathInsideRoot(realResolvedPath, realResolvedRoot);
|
|
11398
|
+
}
|
|
11399
|
+
function isResolvedPathInsideRoot(path, root) {
|
|
11400
|
+
const rel = relative(root, path);
|
|
11401
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
11402
|
+
}
|
|
11403
|
+
async function computeCompressedSessionFingerprint(path) {
|
|
11404
|
+
const hash = createHash("sha256");
|
|
11405
|
+
let bytes = 0;
|
|
11406
|
+
const sink = new Writable({ write(chunk, _encoding, callback) {
|
|
11407
|
+
bytes += chunk.byteLength;
|
|
11408
|
+
hash.update(chunk);
|
|
11409
|
+
callback();
|
|
11410
|
+
} });
|
|
11411
|
+
await pipeline(createReadStream(path), createGzip(), sink);
|
|
11412
|
+
return {
|
|
11413
|
+
bytes,
|
|
11414
|
+
sha256: hash.digest("hex")
|
|
11415
|
+
};
|
|
11416
|
+
}
|
|
11417
|
+
//#endregion
|
|
11418
|
+
//#region src/cli/sync-sessions.ts
|
|
11419
|
+
async function runSyncSessions(argv) {
|
|
11420
|
+
if (isHelpFlag(argv)) {
|
|
11421
|
+
console.log(SYNC_SESSIONS_HELP);
|
|
11422
|
+
return 0;
|
|
11423
|
+
}
|
|
11424
|
+
const { values } = parseArgs({
|
|
11425
|
+
args: argv,
|
|
11426
|
+
options: {
|
|
11427
|
+
...commonOptionDefs(),
|
|
11428
|
+
team: { type: "string" },
|
|
11429
|
+
"runtime-profile-id": { type: "string" },
|
|
11430
|
+
state: { type: "string" },
|
|
11431
|
+
limit: { type: "string" },
|
|
11432
|
+
"dry-run": { type: "boolean" }
|
|
11433
|
+
}
|
|
11434
|
+
});
|
|
11435
|
+
if (!values.team) {
|
|
11436
|
+
console.error("Missing required flag: --team\n");
|
|
11437
|
+
console.error(SYNC_SESSIONS_HELP);
|
|
11438
|
+
return 1;
|
|
11439
|
+
}
|
|
11440
|
+
let opts;
|
|
11441
|
+
try {
|
|
11442
|
+
opts = parseCommonOptions(values);
|
|
11443
|
+
} catch (err) {
|
|
11444
|
+
if (err instanceof MissingRequiredOptionError) {
|
|
11445
|
+
console.error(`${err.message}\n`);
|
|
11446
|
+
console.error(SYNC_SESSIONS_HELP);
|
|
11447
|
+
return 1;
|
|
11448
|
+
}
|
|
11449
|
+
throw err;
|
|
11450
|
+
}
|
|
11451
|
+
const state = parseState(values.state);
|
|
11452
|
+
const limit = parseLimit(values.limit);
|
|
11453
|
+
const agentRootDir = resolve(process.cwd(), values["agent-root"] ?? process.cwd());
|
|
11454
|
+
const ctx = await resolveAgentContext(opts.agent, { agentRootDir });
|
|
11455
|
+
const stateDirs = ensureDaemonStateDirs(agentRootDir);
|
|
11456
|
+
const result = await syncRuntimeSessions({
|
|
11457
|
+
runtimeSessionStore: createApiRuntimeSessionStore({ agent: ctx.agent }),
|
|
11458
|
+
runtimeSlotStore: createApiRuntimeSlotStore({ agent: ctx.agent }),
|
|
11459
|
+
taskReader: ctx.agent.tasks
|
|
11460
|
+
}, {
|
|
11461
|
+
agentName: opts.agent,
|
|
11462
|
+
dryRun: values["dry-run"] === true,
|
|
11463
|
+
limit,
|
|
11464
|
+
runtimeProfileId: values["runtime-profile-id"],
|
|
11465
|
+
sessionRootDir: stateDirs.piSessionsDir,
|
|
11466
|
+
state,
|
|
11467
|
+
teamId: values.team
|
|
11468
|
+
});
|
|
11469
|
+
console.log(JSON.stringify(result, null, 2));
|
|
11470
|
+
return result.failedUpload > 0 || result.unsafeSessionPath > 0 ? 1 : 0;
|
|
11471
|
+
}
|
|
11472
|
+
function parseState(raw) {
|
|
11473
|
+
if (raw === void 0) return void 0;
|
|
11474
|
+
if (raw === "active" || raw === "idle") return raw;
|
|
11475
|
+
throw new Error(`Invalid --state "${raw}": expected active or idle`);
|
|
11476
|
+
}
|
|
11477
|
+
function parseLimit(raw) {
|
|
11478
|
+
if (raw === void 0) return void 0;
|
|
11479
|
+
const value = Number(raw);
|
|
11480
|
+
if (!Number.isInteger(value) || value < 1 || value > 200) throw new Error(`Invalid --limit "${raw}": expected integer 1..200`);
|
|
11481
|
+
return value;
|
|
11482
|
+
}
|
|
11483
|
+
//#endregion
|
|
11148
11484
|
//#region src/main.ts
|
|
11149
11485
|
async function main() {
|
|
11150
11486
|
const [, , subcommand, ...rest] = process.argv;
|
|
@@ -11152,6 +11488,7 @@ async function main() {
|
|
|
11152
11488
|
case "poll": return runPoll(rest);
|
|
11153
11489
|
case "once": return runOnce(rest);
|
|
11154
11490
|
case "drain": return runDrain(rest);
|
|
11491
|
+
case "sync-sessions": return runSyncSessions(rest);
|
|
11155
11492
|
case "-h":
|
|
11156
11493
|
case "--help":
|
|
11157
11494
|
console.log(ROOT_USAGE);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.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.",
|
|
@@ -45,19 +45,19 @@
|
|
|
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/agent-runtime": "0.31.1",
|
|
49
|
+
"@themoltnet/pi-extension": "0.28.0",
|
|
50
|
+
"@themoltnet/sdk": "0.114.0"
|
|
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/bootstrap": "0.1.0",
|
|
57
58
|
"@moltnet/observability": "0.1.0",
|
|
58
59
|
"@moltnet/tasks": "0.1.0",
|
|
59
|
-
"@moltnet/crypto-service": "0.1.0"
|
|
60
|
-
"@moltnet/bootstrap": "0.1.0"
|
|
60
|
+
"@moltnet/crypto-service": "0.1.0"
|
|
61
61
|
},
|
|
62
62
|
"nx": {
|
|
63
63
|
"tags": [
|
|
@@ -74,6 +74,9 @@
|
|
|
74
74
|
"^production",
|
|
75
75
|
"{projectRoot}/vitest.config.e2e.ts",
|
|
76
76
|
"{projectRoot}/e2e/**/*",
|
|
77
|
+
{
|
|
78
|
+
"env": "MOLTNET_AGENT_DAEMON_LIVE_LLM_E2E"
|
|
79
|
+
},
|
|
77
80
|
{
|
|
78
81
|
"externalDependencies": [
|
|
79
82
|
"vitest"
|