@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.
Files changed (3) hide show
  1. package/README.md +7 -5
  2. package/dist/main.js +345 -8
  3. 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 | 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. |
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
- return matches[0];
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.26.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/pi-extension": "0.27.3",
49
- "@themoltnet/sdk": "0.113.1",
50
- "@themoltnet/agent-runtime": "0.31.0"
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"