@themoltnet/agent-daemon 0.25.1 → 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 +458 -63
  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
  /**
@@ -5613,6 +5662,7 @@ var FreeformArtifact = _Object_({
5613
5662
  });
5614
5663
  var FreeformOutput = _Object_({
5615
5664
  summary: String$1({ minLength: 1 }),
5665
+ branch: Optional(String$1({ minLength: 1 })),
5616
5666
  artifacts: Optional(_Array_(FreeformArtifact, { maxItems: 20 })),
5617
5667
  proposedTaskType: Optional(FreeformTaskTypeProposal),
5618
5668
  diaryEntryIds: Optional(_Array_(String$1({ format: "uuid" }))),
@@ -5625,7 +5675,7 @@ var FreeformOutput = _Object_({
5625
5675
  * Server-side preflight for `freeform` task-create. Runs after the
5626
5676
  * sync TypeBox check passes and only kicks in when
5627
5677
  * `input.continueFrom` is set — i.e. the proposer is asking to
5628
- * resume a prior freeform attempt's warm slot (#1287).
5678
+ * continue from a prior freeform attempt (#1287).
5629
5679
  *
5630
5680
  * Failure modes, in evaluation order:
5631
5681
  * 1. `freeform.sourceTaskNotFound` — source task id does not resolve
@@ -5633,23 +5683,17 @@ var FreeformOutput = _Object_({
5633
5683
  * 2. `freeform.sourceTaskTypeNotSupported` — source isn't `freeform`.
5634
5684
  * v1 only supports freeform → freeform continuation.
5635
5685
  * 3. `freeform.sourceAttemptNotCompleted` — named attempt is missing
5636
- * or not in `completed` state; warm continuation only makes sense
5686
+ * or not in `completed` state; continuation only makes sense
5637
5687
  * once the parent has produced a terminal output.
5638
5688
  * 4. `freeform.executionWorkspaceNotInheritable` — caller set
5639
5689
  * `execution.workspace` together with `continueFrom`. Workspace
5640
- * mode for a continuation is inherited from the parent slot
5641
- * (`maybeAttachWarmSlotContext` forces `dedicated_worktree` +
5642
- * the parent's worktreeBranch), so any caller-supplied override
5643
- * is silently dropped at the daemon plan stage. Reject explicitly
5644
- * so misconfiguration surfaces at create time.
5645
- * 5. `freeform.sourceNotResumeEligible` — `daemonState` is null or
5646
- * `slotResumableUntil` is null. Older completions (pre-#1287) and
5647
- * daemons that opt out fall here.
5648
- * 6. `freeform.sourceResumeExpired` — `slotResumableUntil` is in the
5649
- * past; the warm slot's TTL has elapsed and no daemon is
5650
- * guaranteed to still hold it.
5690
+ * mode for a continuation is derived by the daemon from parent runtime
5691
+ * context (local slot first, durable session + source attempt branch
5692
+ * second), so any caller-supplied override is silently dropped at the
5693
+ * daemon plan stage. Reject explicitly so misconfiguration surfaces at
5694
+ * create time.
5651
5695
  *
5652
- * Returns on the first failure (no "report all six") — the checks
5696
+ * Returns on the first failure — the checks
5653
5697
  * are sequential preconditions, later ones presume earlier ones hold.
5654
5698
  */
5655
5699
  async function validateFreeformInputAsync(input, ctx) {
@@ -5668,7 +5712,7 @@ async function validateFreeformInputAsync(input, ctx) {
5668
5712
  }];
5669
5713
  if (input.execution?.workspace) return [{
5670
5714
  field: "input/execution/workspace",
5671
- message: "execution.workspace is inherited from the parent slot when continueFrom is set; omit it",
5715
+ message: "execution.workspace is derived from parent runtime context when continueFrom is set; omit it",
5672
5716
  code: "freeform.executionWorkspaceNotInheritable"
5673
5717
  }];
5674
5718
  if (ctx.deferReadinessChecks) return [];
@@ -5678,17 +5722,6 @@ async function validateFreeformInputAsync(input, ctx) {
5678
5722
  message: `Source attempt ${cf.attemptN} on task ${cf.taskId} is not in 'completed' state`,
5679
5723
  code: "freeform.sourceAttemptNotCompleted"
5680
5724
  }];
5681
- if (!attempt.daemonState || attempt.daemonState.slotResumableUntil === null) return [{
5682
- field: "input/continueFrom",
5683
- message: "Source attempt did not report continuation eligibility (older completion or daemon opted out)",
5684
- code: "freeform.sourceNotResumeEligible"
5685
- }];
5686
- const expiresAt = new Date(attempt.daemonState.slotResumableUntil).getTime();
5687
- if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) return [{
5688
- field: "input/continueFrom",
5689
- message: `Source attempt's warm slot expired at ${attempt.daemonState.slotResumableUntil} (reported at ${attempt.daemonState.reportedAt})`,
5690
- code: "freeform.sourceResumeExpired"
5691
- }];
5692
5725
  return [];
5693
5726
  }
5694
5727
  //#endregion
@@ -8982,11 +9015,12 @@ var MAX_CLAIM_CONDITION_STATUSES = 8;
8982
9015
  /**
8983
9016
  * Daemon-asserted runtime state stamped onto a `TaskAttemptSummary` at
8984
9017
  * attempt-completion time. The server persists this block verbatim and
8985
- * reads `slotResumableUntil` for `tasks_continue` create-time
8986
- * eligibility; the daemon-side claim-affinity filter is the runtime
8987
- * truth. The block carries its own `reportedAt` so consumers can reason
8988
- * about staleness without reading documentation. All daemon-asserted
8989
- * state lives here — top-level attempt fields stay server-authoritative.
9018
+ * exposes `slotResumableUntil` as a legacy/local warm-slot hint; task
9019
+ * continuation eligibility is based on the completed source attempt and
9020
+ * daemon-side claim-affinity/runtime-session recovery. The block carries
9021
+ * its own `reportedAt` so consumers can reason about staleness without
9022
+ * reading documentation. All daemon-asserted state lives here —
9023
+ * top-level attempt fields stay server-authoritative.
8990
9024
  *
8991
9025
  * Adding new fields requires explicit design review (intentional
8992
9026
  * boundary; see docs/superpowers/specs/2026-06-04-tasks-continue-design.md).
@@ -9250,6 +9284,8 @@ Commands:
9250
9284
  once Claim and execute one specific queued task by id, then exit.
9251
9285
  drain Poll until the queue has nothing claimable, then exit.
9252
9286
  Useful for batch eval runs and demos.
9287
+ sync-sessions
9288
+ Repair durable runtime-session checkpoints from local slot files.
9253
9289
 
9254
9290
  Run \`agent-daemon <command> --help\` for command-specific flags.
9255
9291
 
@@ -9340,6 +9376,34 @@ Example:
9340
9376
  --task-types judge_pack \\
9341
9377
  --agent legreffier \\
9342
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`;
9343
9407
  function isHelpFlag(args) {
9344
9408
  return args.includes("--help") || args.includes("-h");
9345
9409
  }
@@ -9633,12 +9697,13 @@ var ProducerContextResolutionError = class extends Error {
9633
9697
  function createExecutionPlanCache(args) {
9634
9698
  const cache = /* @__PURE__ */ new Map();
9635
9699
  const runtimeSessionStore = args.runtimeSessionStore ?? createNullRuntimeSessionStore();
9700
+ const sourceAttemptResolver = args.sourceAttemptResolver ?? createNullSourceAttemptResolver();
9636
9701
  return {
9637
9702
  async getOrCreate(claimedTask) {
9638
9703
  const key = buildClaimedTaskKey(claimedTask);
9639
9704
  const existing = cache.get(key);
9640
9705
  if (existing) return existing;
9641
- const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec, args.workspacePolicy), args.stateDirs, args.slotRegistry, runtimeSessionStore);
9706
+ const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec, args.workspacePolicy), args.stateDirs, args.slotRegistry, runtimeSessionStore, sourceAttemptResolver);
9642
9707
  assertPlanAllowedByWorkspacePolicy(plan, args.workspacePolicy);
9643
9708
  cache.set(key, plan);
9644
9709
  return plan;
@@ -9648,13 +9713,18 @@ function createExecutionPlanCache(args) {
9648
9713
  }
9649
9714
  };
9650
9715
  }
9716
+ function createNullSourceAttemptResolver() {
9717
+ return { findOutputBranch() {
9718
+ return Promise.resolve(null);
9719
+ } };
9720
+ }
9651
9721
  function createNullRuntimeSessionStore() {
9652
9722
  return {
9653
- async findRuntimeSessionByTaskAttempt() {
9654
- return null;
9723
+ findRuntimeSessionByTaskAttempt() {
9724
+ return Promise.resolve(null);
9655
9725
  },
9656
- async hydrateSession() {
9657
- throw new ProducerContextResolutionError("Cannot hydrate runtime session: no runtime session store configured");
9726
+ hydrateSession() {
9727
+ return Promise.reject(new ProducerContextResolutionError("Cannot hydrate runtime session: no runtime session store configured"));
9658
9728
  },
9659
9729
  async uploadAttemptFinal() {}
9660
9730
  };
@@ -9676,17 +9746,26 @@ function planToRuntimeProfileWorkspaceMode(plan) {
9676
9746
  function buildClaimedTaskKey(task) {
9677
9747
  return `${task.task.id}:${task.attemptN}`;
9678
9748
  }
9679
- async function resolveWarmSlot(slotRegistry, runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs) {
9680
- const producerContext = await slotRegistry.findLatestSlotByTaskAttempt(teamId, sourceTaskId, sourceAttemptN);
9681
- if (!producerContext) return { kind: "missing" };
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({
9749
+ async function hydrateRemoteRuntimeSession(runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs) {
9750
+ if (!await runtimeSessionStore.findRuntimeSessionByTaskAttempt(teamId, sourceTaskId, sourceAttemptN)) return null;
9751
+ return runtimeSessionStore.hydrateSession({
9685
9752
  attemptN: sourceAttemptN,
9686
9753
  destinationDir: `${stateDirs.piSessionsDir}/remote-${sourceTaskId}-attempt-${sourceAttemptN}`,
9687
9754
  taskId: sourceTaskId,
9688
9755
  teamId
9689
- }) : null;
9756
+ });
9757
+ }
9758
+ async function resolveWarmSlot(slotRegistry, runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs) {
9759
+ const producerContext = await slotRegistry.findLatestSlotByTaskAttempt(teamId, sourceTaskId, sourceAttemptN);
9760
+ if (!producerContext) {
9761
+ const remoteSessionPath = await hydrateRemoteRuntimeSession(runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs);
9762
+ return remoteSessionPath ? {
9763
+ kind: "remote-session",
9764
+ sessionPath: remoteSessionPath
9765
+ } : { kind: "missing" };
9766
+ }
9767
+ const localSessionPath = resolveProducerSessionPath(producerContext);
9768
+ const sourceSessionPath = localSessionPath ? localSessionPath : await hydrateRemoteRuntimeSession(runtimeSessionStore, teamId, sourceTaskId, sourceAttemptN, stateDirs);
9690
9769
  if (!sourceSessionPath) return { kind: "no-session-path" };
9691
9770
  return {
9692
9771
  kind: "found",
@@ -9695,19 +9774,54 @@ async function resolveWarmSlot(slotRegistry, runtimeSessionStore, teamId, source
9695
9774
  workspacePath: resolveProducerWorkspaceCopySource(producerContext, stateDirs)
9696
9775
  };
9697
9776
  }
9698
- async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slotRegistry, runtimeSessionStore) {
9777
+ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slotRegistry, runtimeSessionStore, sourceAttemptResolver) {
9699
9778
  if (claimedTask.task.taskType === "freeform") {
9700
9779
  const continueFrom = claimedTask.task.input.continueFrom;
9701
9780
  if (!continueFrom) return basePlan;
9702
9781
  const resolution = await resolveWarmSlot(slotRegistry, runtimeSessionStore, claimedTask.task.teamId, continueFrom.taskId, continueFrom.attemptN, stateDirs);
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`);
9782
+ if (resolution.kind === "missing") throw new ProducerContextResolutionError(`Continuation source task ${continueFrom.taskId} attempt ${continueFrom.attemptN} has no local runtime slot or durable runtime session — claim affinity filter should have prevented this claim`);
9704
9783
  if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Continuation source attempt ${continueFrom.taskId}/${continueFrom.attemptN} has no persisted Pi session path`);
9705
9784
  const sessionDir = `${stateDirs.piSessionsDir}/continue-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
9785
+ if (resolution.kind === "remote-session") {
9786
+ const recoveredBranch = await sourceAttemptResolver.findOutputBranch({
9787
+ attemptN: continueFrom.attemptN,
9788
+ taskId: continueFrom.taskId
9789
+ });
9790
+ if (continueFrom.mode === "fork") {
9791
+ if (recoveredBranch) {
9792
+ const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
9793
+ const forkBranch = buildForkBranch(recoveredBranch, claimedTask.task.id, claimedTask.attemptN);
9794
+ return {
9795
+ ...basePlan,
9796
+ workspaceMode: "dedicated_worktree",
9797
+ workspaceId: forkWorkspaceId,
9798
+ worktreeBranch: forkBranch,
9799
+ worktreeBaseRef: recoveredBranch,
9800
+ workspaceKind: "fork",
9801
+ sessionPersistence: {
9802
+ sessionDir,
9803
+ forkFromSessionPath: resolution.sessionPath
9804
+ }
9805
+ };
9806
+ }
9807
+ throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: durable runtime session is available but the source attempt output did not report a branch`);
9808
+ }
9809
+ return {
9810
+ ...basePlan,
9811
+ workspaceMode: "dedicated_worktree",
9812
+ workspaceId: recoveredBranch ? `extend-${continueFrom.taskId}-attempt-${continueFrom.attemptN}` : null,
9813
+ worktreeBranch: recoveredBranch,
9814
+ sessionPersistence: {
9815
+ sessionDir,
9816
+ forkFromSessionPath: resolution.sessionPath
9817
+ }
9818
+ };
9819
+ }
9706
9820
  const parentBranch = resolution.producerSlot.workspace?.worktreeBranch ?? null;
9707
9821
  if (continueFrom.mode === "fork") {
9708
9822
  if (!parentBranch) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch to fork from`);
9709
9823
  const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
9710
- const forkBranch = `${parentBranch}-fork-${claimedTask.task.id.slice(0, 8)}-${claimedTask.attemptN}`;
9824
+ const forkBranch = buildForkBranch(parentBranch, claimedTask.task.id, claimedTask.attemptN);
9711
9825
  return {
9712
9826
  ...basePlan,
9713
9827
  workspaceMode: "dedicated_worktree",
@@ -9739,6 +9853,7 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
9739
9853
  const resolution = await resolveWarmSlot(slotRegistry, runtimeSessionStore, claimedTask.task.teamId, targetTaskId, targetAttemptN, stateDirs);
9740
9854
  if (resolution.kind === "missing") throw new ProducerContextResolutionError(`No live producer runtime slot found for task ${targetTaskId} attempt ${targetAttemptN}`);
9741
9855
  if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Producer task ${targetTaskId} attempt ${targetAttemptN} has no persisted Pi session path`);
9856
+ if (resolution.kind === "remote-session") throw new ProducerContextResolutionError(`Producer task ${targetTaskId} attempt ${targetAttemptN} has a durable runtime session but no workspace metadata to copy`);
9742
9857
  return {
9743
9858
  ...basePlan,
9744
9859
  workspaceMode: "scratch_mount",
@@ -9754,6 +9869,9 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
9754
9869
  }
9755
9870
  };
9756
9871
  }
9872
+ function buildForkBranch(parentBranch, childTaskId, childAttemptN) {
9873
+ return `${parentBranch}-fork-${childTaskId.slice(0, 8)}-${childAttemptN}`;
9874
+ }
9757
9875
  function resolveProducerSessionPath(producer) {
9758
9876
  const explicit = producer.session?.sessionPath ?? null;
9759
9877
  if (explicit && existsSync(explicit)) return explicit;
@@ -9783,12 +9901,11 @@ function recoverScratchWorkspacePath(producer, stateDirs) {
9783
9901
  //#endregion
9784
9902
  //#region src/lib/finalize.ts
9785
9903
  /**
9786
- * Build the `daemonState` payload for a `/complete` call. Only freeform
9787
- * attempts that ran with a runtime slot are eligible for continuation
9788
- * (`tasks_continue`, see issue #1287). Other task types and slot-less
9789
- * freeform completions report `null` for `slotResumableUntil`, which
9790
- * the server persists verbatim — continuations against such attempts
9791
- * fail validation with `freeform.sourceNotResumeEligible`.
9904
+ * Build the `daemonState` payload for a `/complete` call. Freeform
9905
+ * attempts report the local warm-slot hint when one exists; slot-less
9906
+ * freeform completions report `null` for `slotResumableUntil`. The server
9907
+ * persists this verbatim as diagnostic/runtime metadata, not as the
9908
+ * continuation eligibility gate.
9792
9909
  *
9793
9910
  * Returns `null` for non-freeform task types so the field is omitted
9794
9911
  * from the request body (the server treats null and absent the same).
@@ -10082,6 +10199,11 @@ async function resolveRuntimeProfile(options) {
10082
10199
  teamId: profile.teamId,
10083
10200
  provider: profile.provider,
10084
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,
10085
10207
  leaseTtlSec: profile.leaseTtlSec,
10086
10208
  heartbeatIntervalMs: profile.heartbeatIntervalMs,
10087
10209
  maxBatchSize: profile.maxBatchSize,
@@ -10127,7 +10249,15 @@ async function resolveProfileByName(options) {
10127
10249
  const matches = (await options.agent.runtimeProfiles.list({ teamId: options.teamId })).items.filter((item) => item.name === options.profile);
10128
10250
  if (matches.length === 0) throw new Error(`Runtime profile "${options.profile}" was not found in team ${options.teamId}.`);
10129
10251
  if (matches.length > 1) throw new Error(`Runtime profile name "${options.profile}" is ambiguous in team ${options.teamId}. Use the profile UUID instead.`);
10130
- 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
+ };
10131
10261
  }
10132
10262
  function isExecutableOnPath(tool, pathValue) {
10133
10263
  if (tool.includes("/")) return isExecutable(isAbsolute(tool) ? tool : resolve(process.cwd(), tool));
@@ -10268,6 +10398,34 @@ function createApiRuntimeSlotStore(args) {
10268
10398
  } : null
10269
10399
  };
10270
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
+ },
10271
10429
  async close() {}
10272
10430
  };
10273
10431
  }
@@ -10304,6 +10462,21 @@ function signalExitCode(signal) {
10304
10462
  return signal === "SIGINT" ? 130 : 143;
10305
10463
  }
10306
10464
  //#endregion
10465
+ //#region src/lib/source-attempts.ts
10466
+ function createApiSourceAttemptResolver(args) {
10467
+ const { agent } = args;
10468
+ return { async findOutputBranch(input) {
10469
+ const attempt = (await agent.tasks.listAttempts(input.taskId)).find((candidate) => candidate.attemptN === input.attemptN);
10470
+ if (!attempt || attempt.status !== "completed") return null;
10471
+ return resolveOutputBranch(attempt.output);
10472
+ } };
10473
+ }
10474
+ function resolveOutputBranch(output) {
10475
+ if (!output || typeof output !== "object") return null;
10476
+ const branch = output.branch;
10477
+ return typeof branch === "string" && branch.length > 0 ? branch : null;
10478
+ }
10479
+ //#endregion
10307
10480
  //#region src/lib/state-dir.ts
10308
10481
  function ensureDaemonStateDirs(mountPath) {
10309
10482
  const rootDir = join(mountPath, ".moltnet", "d");
@@ -10411,6 +10584,7 @@ async function runPolling(opts) {
10411
10584
  for (const profile of profiles) validateRuntimeProfilePrerequisites(profile, cfg.profilePrerequisiteEnv, cfg.profilePrerequisitePath);
10412
10585
  const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
10413
10586
  const runtimeSessionStore = createApiRuntimeSessionStore({ agent: ctx.agent });
10587
+ const sourceAttemptResolver = createApiSourceAttemptResolver({ agent: ctx.agent });
10414
10588
  const runtimes = /* @__PURE__ */ new Map();
10415
10589
  for (const profile of profiles) {
10416
10590
  const common = parseCommonOptions(values, { runtimeDefaults: {
@@ -10441,7 +10615,8 @@ async function runPolling(opts) {
10441
10615
  allowedWorkspaceModes: profile.allowedWorkspaceModes
10442
10616
  },
10443
10617
  slotRegistry,
10444
- runtimeSessionStore
10618
+ runtimeSessionStore,
10619
+ sourceAttemptResolver
10445
10620
  });
10446
10621
  runtimes.set(profile.id, {
10447
10622
  common,
@@ -10514,6 +10689,11 @@ async function runPolling(opts) {
10514
10689
  name: profile.name,
10515
10690
  provider: profile.provider,
10516
10691
  model: profile.model,
10692
+ thinkingLevel: profile.thinkingLevel,
10693
+ temperature: profile.temperature,
10694
+ topP: profile.topP,
10695
+ topK: profile.topK,
10696
+ maxOutputTokens: profile.maxOutputTokens,
10517
10697
  sandbox: runtime.sandbox.path,
10518
10698
  leaseTtlSec: runtime.common.leaseTtlSec,
10519
10699
  heartbeatIntervalMs: runtime.common.heartbeatIntervalMs,
@@ -10551,7 +10731,8 @@ async function runPolling(opts) {
10551
10731
  debug: baseCommon.debug,
10552
10732
  logger: rootLogger,
10553
10733
  slotRegistry,
10554
- sessionRegistry: runtimeSessionStore
10734
+ sessionRegistry: runtimeSessionStore,
10735
+ sourceAttemptResolver
10555
10736
  }),
10556
10737
  makeReporter: (claimedTask) => {
10557
10738
  const selected = runtimeForClaimedTask(runtimes, claimedTask);
@@ -10606,7 +10787,12 @@ async function runPolling(opts) {
10606
10787
  runtimeProfileId: profile.id,
10607
10788
  runtimeProfileName: profile.name,
10608
10789
  provider: profile.provider,
10609
- 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
10610
10796
  });
10611
10797
  let executionPlan;
10612
10798
  try {
@@ -10707,6 +10893,11 @@ async function runPolling(opts) {
10707
10893
  mountPath: sandbox.rootDir,
10708
10894
  provider: profile.provider,
10709
10895
  model: profile.model,
10896
+ thinkingLevel: profile.thinkingLevel,
10897
+ temperature: profile.temperature,
10898
+ topP: profile.topP,
10899
+ topK: profile.topK,
10900
+ maxOutputTokens: profile.maxOutputTokens,
10710
10901
  sandboxConfig: sandbox.config,
10711
10902
  makeExecutionPlan: (task) => executionPlans.getOrCreate(task),
10712
10903
  makeOnTurnEvent: makeTurnEventHandlerFactory(taskLogger),
@@ -10722,7 +10913,12 @@ async function runPolling(opts) {
10722
10913
  profileId: profile.id,
10723
10914
  profileName: profile.name,
10724
10915
  provider: profile.provider,
10725
- 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
10726
10922
  }, () => rawExecuteTask(claimedTask, reporter));
10727
10923
  } finally {
10728
10924
  active = null;
@@ -10853,6 +11049,7 @@ async function runOnce(argv) {
10853
11049
  const stateDirs = ensureDaemonStateDirs(sandbox.rootDir);
10854
11050
  const slotRegistry = createApiRuntimeSlotStore({ agent: ctx.agent });
10855
11051
  const runtimeSessionStore = createApiRuntimeSessionStore({ agent: ctx.agent });
11052
+ const sourceAttemptResolver = createApiSourceAttemptResolver({ agent: ctx.agent });
10856
11053
  const slotIdentity = {
10857
11054
  agentName: opts.agent,
10858
11055
  runtimeProfileId: profile.id
@@ -10866,7 +11063,8 @@ async function runOnce(argv) {
10866
11063
  allowedWorkspaceModes: profile.allowedWorkspaceModes
10867
11064
  },
10868
11065
  slotRegistry,
10869
- runtimeSessionStore
11066
+ runtimeSessionStore,
11067
+ sourceAttemptResolver
10870
11068
  });
10871
11069
  const otelShutdown = await initWorkerOtel({
10872
11070
  serviceName: "moltnet.agent-daemon.once",
@@ -10877,6 +11075,11 @@ async function runOnce(argv) {
10877
11075
  "moltnet.agent.name": opts.agent,
10878
11076
  "moltnet.llm.provider": profile.provider,
10879
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) } : {},
10880
11083
  "moltnet.runtime_profile.id": profile.id
10881
11084
  }
10882
11085
  });
@@ -10889,6 +11092,11 @@ async function runOnce(argv) {
10889
11092
  agent: opts.agent,
10890
11093
  provider: profile.provider,
10891
11094
  model: profile.model,
11095
+ thinkingLevel: profile.thinkingLevel,
11096
+ temperature: profile.temperature,
11097
+ topP: profile.topP,
11098
+ topK: profile.topK,
11099
+ maxOutputTokens: profile.maxOutputTokens,
10892
11100
  runtimeProfileId: profile.id,
10893
11101
  runtimeProfileName: profile.name
10894
11102
  });
@@ -10939,6 +11147,11 @@ async function runOnce(argv) {
10939
11147
  mountPath: sandbox.rootDir,
10940
11148
  provider: profile.provider,
10941
11149
  model: profile.model,
11150
+ thinkingLevel: profile.thinkingLevel,
11151
+ temperature: profile.temperature,
11152
+ topP: profile.topP,
11153
+ topK: profile.topK,
11154
+ maxOutputTokens: profile.maxOutputTokens,
10942
11155
  sandboxConfig: sandbox.config,
10943
11156
  makeExecutionPlan: (claimedTask) => executionPlans.getOrCreate(claimedTask),
10944
11157
  onTurnEvent: makeTurnEventHandler(rootLogger, { taskId }),
@@ -10997,7 +11210,12 @@ async function runOnce(argv) {
10997
11210
  profileId: profile.id,
10998
11211
  profileName: profile.name,
10999
11212
  provider: profile.provider,
11000
- 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
11001
11219
  }, () => rawExecuteTask(claimedTask, reporter));
11002
11220
  } finally {
11003
11221
  activeAttemptN = null;
@@ -11087,6 +11305,182 @@ function runPoll(argv) {
11087
11305
  });
11088
11306
  }
11089
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
11090
11484
  //#region src/main.ts
11091
11485
  async function main() {
11092
11486
  const [, , subcommand, ...rest] = process.argv;
@@ -11094,6 +11488,7 @@ async function main() {
11094
11488
  case "poll": return runPoll(rest);
11095
11489
  case "once": return runOnce(rest);
11096
11490
  case "drain": return runDrain(rest);
11491
+ case "sync-sessions": return runSyncSessions(rest);
11097
11492
  case "-h":
11098
11493
  case "--help":
11099
11494
  console.log(ROOT_USAGE);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.25.1",
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,9 +45,9 @@
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.2",
49
- "@themoltnet/sdk": "0.113.1",
50
- "@themoltnet/agent-runtime": "0.30.1"
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",
@@ -55,9 +55,9 @@
55
55
  "vite": "^8.0.0",
56
56
  "vitest": "^3.0.0",
57
57
  "@moltnet/bootstrap": "0.1.0",
58
- "@moltnet/crypto-service": "0.1.0",
59
58
  "@moltnet/observability": "0.1.0",
60
- "@moltnet/tasks": "0.1.0"
59
+ "@moltnet/tasks": "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"