@themoltnet/agent-daemon 0.23.0 → 0.24.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 (2) hide show
  1. package/dist/main.js +101 -14
  2. package/package.json +6 -6
package/dist/main.js CHANGED
@@ -4399,6 +4399,16 @@ var RuntimeProfileToolName = String$1({
4399
4399
  maxLength: 128,
4400
4400
  pattern: "^[a-zA-Z0-9._/-]+$"
4401
4401
  });
4402
+ var RuntimeProfileWorkspaceMode = Union([
4403
+ Literal("none"),
4404
+ Literal("shared_mount"),
4405
+ Literal("dedicated_worktree")
4406
+ ]);
4407
+ var RuntimeProfileAllowedWorkspaceModes = _Array_(RuntimeProfileWorkspaceMode, {
4408
+ minItems: 1,
4409
+ maxItems: 3,
4410
+ uniqueItems: true
4411
+ });
4402
4412
  var SandboxResumeCommandWhenSchema = _Object_({ workspaceMode: Optional(_Array_(Union([
4403
4413
  Literal("shared_mount"),
4404
4414
  Literal("dedicated_worktree"),
@@ -4502,6 +4512,14 @@ var RuntimeProfileMaxBatchSize = Integer({
4502
4512
  minimum: 1,
4503
4513
  maximum: 1e3
4504
4514
  });
4515
+ var RuntimeProfileMaxTurns = Integer({
4516
+ minimum: 0,
4517
+ maximum: 1e4
4518
+ });
4519
+ var RuntimeProfileMaxBashTimeouts = Integer({
4520
+ minimum: 0,
4521
+ maximum: 1e3
4522
+ });
4505
4523
  _Object_({
4506
4524
  id: String$1({ format: "uuid" }),
4507
4525
  teamId: String$1({ format: "uuid" }),
@@ -4519,6 +4537,8 @@ _Object_({
4519
4537
  sandbox: RuntimeProfileSandbox,
4520
4538
  sessionStorageMode: Literal("local"),
4521
4539
  workspaceStorageMode: Literal("local"),
4540
+ defaultWorkspaceMode: Union([RuntimeProfileWorkspaceMode, Null()]),
4541
+ allowedWorkspaceModes: RuntimeProfileAllowedWorkspaceModes,
4522
4542
  sessionTtlSec: Integer({
4523
4543
  minimum: 1,
4524
4544
  maximum: 86400
@@ -4530,6 +4550,8 @@ _Object_({
4530
4550
  leaseTtlSec: RuntimeProfileLeaseTtlSec,
4531
4551
  heartbeatIntervalMs: RuntimeProfileHeartbeatIntervalMs,
4532
4552
  maxBatchSize: RuntimeProfileMaxBatchSize,
4553
+ maxTurns: RuntimeProfileMaxTurns,
4554
+ maxBashTimeouts: RuntimeProfileMaxBashTimeouts,
4533
4555
  requiredEnv: _Array_(RuntimeProfileEnvName, { maxItems: 100 }),
4534
4556
  requiredTools: _Array_(RuntimeProfileToolName, { maxItems: 100 }),
4535
4557
  context: _Array_(RuntimeProfileContext, { maxItems: 5 }),
@@ -9449,9 +9471,9 @@ function slugifySessionComponent(input) {
9449
9471
  }
9450
9472
  //#endregion
9451
9473
  //#region src/lib/task-execution-plan.ts
9452
- function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlSec) {
9474
+ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlSec, runtimeProfileWorkspacePolicy = {}) {
9453
9475
  const descriptor = deriveTaskSessionDescriptor(task);
9454
- const workspaceMode = resolveTaskWorkspaceMode(task, descriptor.policy);
9476
+ const workspaceMode = resolveTaskWorkspaceMode(task, descriptor.policy, runtimeProfileWorkspacePolicy);
9455
9477
  const slotKey = warmSessionTtlSec > 0 ? descriptor.sessionKey : null;
9456
9478
  const workspaceScope = slotKey !== null ? descriptor.policy.workspaceScope : "attempt";
9457
9479
  const slotId = slotKey ? buildDaemonSlotId(identity, slotKey) : null;
@@ -9501,14 +9523,40 @@ function resolveTaskWorktreeBranch(task, workspaceMode) {
9501
9523
  }
9502
9524
  return `task/${slugifyAsciiLower(task.taskType, 60) || "task"}-${task.id.slice(0, 8)}`;
9503
9525
  }
9504
- function resolveTaskWorkspaceMode(task, policy) {
9505
- if (!policy.acceptsInputWorkspaceOverride) return policy.workspaceMode;
9506
- switch (typeof task.input.execution?.workspace === "string" ? task.input.execution.workspace : null) {
9507
- case "none": return "scratch_mount";
9508
- case "shared_mount": return "shared_mount";
9509
- case "dedicated_worktree": return "dedicated_worktree";
9510
- default: return policy.workspaceMode;
9511
- }
9526
+ function resolveTaskWorkspaceMode(task, policy, runtimeProfileWorkspacePolicy) {
9527
+ const allowed = resolveAllowedWorkspaceModes(runtimeProfileWorkspacePolicy);
9528
+ const profileDefault = runtimeProfileWorkspacePolicy.defaultWorkspaceMode ?? null;
9529
+ const requestedWorkspace = policy.acceptsInputWorkspaceOverride && typeof task.input.execution?.workspace === "string" ? task.input.execution.workspace : null;
9530
+ if (isRuntimeProfileWorkspaceMode(requestedWorkspace)) {
9531
+ if (allowed.has(requestedWorkspace)) return toDaemonWorkspaceMode(requestedWorkspace);
9532
+ }
9533
+ if (profileDefault && allowed.has(profileDefault)) return toDaemonWorkspaceMode(profileDefault);
9534
+ if (allowed.has(policy.workspaceMode)) return policy.workspaceMode;
9535
+ return toDaemonWorkspaceMode(firstAllowedWorkspaceMode(allowed));
9536
+ }
9537
+ var ALL_WORKSPACE_MODES = [
9538
+ "none",
9539
+ "shared_mount",
9540
+ "dedicated_worktree"
9541
+ ];
9542
+ var WORKSPACE_MODE_FALLBACK_ORDER = [
9543
+ "none",
9544
+ "dedicated_worktree",
9545
+ "shared_mount"
9546
+ ];
9547
+ function resolveAllowedWorkspaceModes(policy) {
9548
+ const modes = policy.allowedWorkspaceModes && policy.allowedWorkspaceModes.length > 0 ? policy.allowedWorkspaceModes : ALL_WORKSPACE_MODES;
9549
+ return new Set(modes.filter((mode) => isRuntimeProfileWorkspaceMode(mode)));
9550
+ }
9551
+ function firstAllowedWorkspaceMode(allowed) {
9552
+ for (const mode of WORKSPACE_MODE_FALLBACK_ORDER) if (allowed.has(mode)) return mode;
9553
+ return "none";
9554
+ }
9555
+ function isRuntimeProfileWorkspaceMode(value) {
9556
+ return value === "none" || value === "shared_mount" || value === "dedicated_worktree";
9557
+ }
9558
+ function toDaemonWorkspaceMode(mode) {
9559
+ return mode === "none" ? "scratch_mount" : mode;
9512
9560
  }
9513
9561
  function resolveTaskWorkspaceId(task, executionPlan) {
9514
9562
  if (executionPlan.workspaceScope === "session" && executionPlan.sessionKey !== null) return `session-${encodeURIComponent(executionPlan.sessionKey)}`;
@@ -9529,7 +9577,8 @@ function createExecutionPlanCache(args) {
9529
9577
  const key = buildClaimedTaskKey(claimedTask);
9530
9578
  const existing = cache.get(key);
9531
9579
  if (existing) return existing;
9532
- const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec), args.stateDirs, args.slotRegistry);
9580
+ const plan = await maybeAttachWarmSlotContext(claimedTask, buildDaemonTaskExecutionPlan(claimedTask.task, args.stateDirs, args.slotIdentity, args.warmSessionTtlSec, args.workspacePolicy), args.stateDirs, args.slotRegistry);
9581
+ assertPlanAllowedByWorkspacePolicy(plan, args.workspacePolicy);
9533
9582
  cache.set(key, plan);
9534
9583
  return plan;
9535
9584
  },
@@ -9538,6 +9587,20 @@ function createExecutionPlanCache(args) {
9538
9587
  }
9539
9588
  };
9540
9589
  }
9590
+ function assertPlanAllowedByWorkspacePolicy(plan, policy) {
9591
+ const allowed = new Set(policy?.allowedWorkspaceModes && policy.allowedWorkspaceModes.length > 0 ? policy.allowedWorkspaceModes : [
9592
+ "none",
9593
+ "shared_mount",
9594
+ "dedicated_worktree"
9595
+ ]);
9596
+ const effectiveMode = planToRuntimeProfileWorkspaceMode(plan);
9597
+ if (!allowed.has(effectiveMode)) throw new Error(`Runtime profile forbids final workspace mode "${effectiveMode}" for this task`);
9598
+ }
9599
+ function planToRuntimeProfileWorkspaceMode(plan) {
9600
+ if (plan.workspaceMode === "scratch_mount") return "none";
9601
+ if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch) return "shared_mount";
9602
+ return plan.workspaceMode;
9603
+ }
9541
9604
  function buildClaimedTaskKey(task) {
9542
9605
  return `${task.task.id}:${task.attemptN}`;
9543
9606
  }
@@ -9798,6 +9861,8 @@ function parseCommonOptions(args, options = {}) {
9798
9861
  leaseTtlSec: options.runtimeDefaults?.leaseTtlSec ?? DEFAULTS.leaseTtlSec,
9799
9862
  heartbeatIntervalMs: options.runtimeDefaults?.heartbeatIntervalMs ?? DEFAULTS.heartbeatIntervalMs,
9800
9863
  maxBatchSize: options.runtimeDefaults?.maxBatchSize ?? DEFAULTS.maxBatchSize,
9864
+ maxTurns: options.runtimeDefaults?.maxTurns ?? DEFAULTS.maxTurns,
9865
+ maxBashTimeouts: options.runtimeDefaults?.maxBashTimeouts ?? DEFAULTS.maxBashTimeouts,
9801
9866
  warmSessionTtlSec: options.runtimeDefaults?.warmSessionTtlSec ?? DEFAULTS.warmSessionTtlSec
9802
9867
  };
9803
9868
  if (!args.agent) throw new MissingRequiredOptionError("agent");
@@ -9808,8 +9873,8 @@ function parseCommonOptions(args, options = {}) {
9808
9873
  heartbeatIntervalMs: parseNonNegativeInt(args["heartbeat-interval-ms"], "heartbeat-interval-ms", runtimeDefaults.heartbeatIntervalMs),
9809
9874
  maxBatchSize: parsePositiveInt(args["max-batch-size"], "max-batch-size", runtimeDefaults.maxBatchSize),
9810
9875
  flushIntervalMs: parseNonNegativeInt(args["flush-interval-ms"], "flush-interval-ms", DEFAULTS.flushIntervalMs),
9811
- maxTurns: parseNonNegativeInt(args["max-turns"], "max-turns", DEFAULTS.maxTurns),
9812
- maxBashTimeouts: parseNonNegativeInt(args["max-bash-timeouts"], "max-bash-timeouts", DEFAULTS.maxBashTimeouts),
9876
+ maxTurns: parseNonNegativeInt(args["max-turns"], "max-turns", runtimeDefaults.maxTurns),
9877
+ maxBashTimeouts: parseNonNegativeInt(args["max-bash-timeouts"], "max-bash-timeouts", runtimeDefaults.maxBashTimeouts),
9813
9878
  warmSessionTtlSec: parseNonNegativeInt(args["warm-session-ttl-sec"], "warm-session-ttl-sec", runtimeDefaults.warmSessionTtlSec),
9814
9879
  debug: args.debug === true
9815
9880
  };
@@ -9941,8 +10006,12 @@ async function resolveRuntimeProfile(options) {
9941
10006
  leaseTtlSec: profile.leaseTtlSec,
9942
10007
  heartbeatIntervalMs: profile.heartbeatIntervalMs,
9943
10008
  maxBatchSize: profile.maxBatchSize,
10009
+ maxTurns: profile.maxTurns,
10010
+ maxBashTimeouts: profile.maxBashTimeouts,
9944
10011
  sessionTtlSec: profile.sessionTtlSec,
9945
10012
  workspaceTtlSec: profile.workspaceTtlSec,
10013
+ defaultWorkspaceMode: profile.defaultWorkspaceMode ?? null,
10014
+ allowedWorkspaceModes: profile.allowedWorkspaceModes,
9946
10015
  requiredEnv: profile.requiredEnv,
9947
10016
  requiredTools: profile.requiredTools,
9948
10017
  sandboxConfig: profile.sandbox,
@@ -10200,6 +10269,8 @@ async function runPolling(opts) {
10200
10269
  leaseTtlSec: profile.leaseTtlSec,
10201
10270
  heartbeatIntervalMs: profile.heartbeatIntervalMs,
10202
10271
  maxBatchSize: profile.maxBatchSize,
10272
+ maxTurns: profile.maxTurns,
10273
+ maxBashTimeouts: profile.maxBashTimeouts,
10203
10274
  warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
10204
10275
  } });
10205
10276
  const sandbox = {
@@ -10217,6 +10288,10 @@ async function runPolling(opts) {
10217
10288
  stateDirs,
10218
10289
  slotIdentity,
10219
10290
  warmSessionTtlSec: common.warmSessionTtlSec,
10291
+ workspacePolicy: {
10292
+ defaultWorkspaceMode: profile.defaultWorkspaceMode,
10293
+ allowedWorkspaceModes: profile.allowedWorkspaceModes
10294
+ },
10220
10295
  slotRegistry
10221
10296
  });
10222
10297
  runtimes.set(profile.id, {
@@ -10293,9 +10368,13 @@ async function runPolling(opts) {
10293
10368
  sandbox: runtime.sandbox.path,
10294
10369
  leaseTtlSec: runtime.common.leaseTtlSec,
10295
10370
  heartbeatIntervalMs: runtime.common.heartbeatIntervalMs,
10371
+ maxTurns: runtime.common.maxTurns,
10372
+ maxBashTimeouts: runtime.common.maxBashTimeouts,
10296
10373
  warmSessionTtlSec: runtime.common.warmSessionTtlSec,
10297
10374
  profileSessionTtlSec: profile.sessionTtlSec,
10298
- profileWorkspaceTtlSec: profile.workspaceTtlSec
10375
+ profileWorkspaceTtlSec: profile.workspaceTtlSec,
10376
+ defaultWorkspaceMode: profile.defaultWorkspaceMode,
10377
+ allowedWorkspaceModes: profile.allowedWorkspaceModes
10299
10378
  };
10300
10379
  }),
10301
10380
  piAgentDir: piAgentDir.path,
@@ -10589,6 +10668,8 @@ async function runOnce(argv) {
10589
10668
  leaseTtlSec: profile.leaseTtlSec,
10590
10669
  heartbeatIntervalMs: profile.heartbeatIntervalMs,
10591
10670
  maxBatchSize: profile.maxBatchSize,
10671
+ maxTurns: profile.maxTurns,
10672
+ maxBashTimeouts: profile.maxBashTimeouts,
10592
10673
  warmSessionTtlSec: resolveProfileWarmSessionTtlSec(profile)
10593
10674
  } });
10594
10675
  const sandbox = {
@@ -10608,6 +10689,10 @@ async function runOnce(argv) {
10608
10689
  stateDirs,
10609
10690
  slotIdentity,
10610
10691
  warmSessionTtlSec: opts.warmSessionTtlSec,
10692
+ workspacePolicy: {
10693
+ defaultWorkspaceMode: profile.defaultWorkspaceMode,
10694
+ allowedWorkspaceModes: profile.allowedWorkspaceModes
10695
+ },
10611
10696
  slotRegistry
10612
10697
  });
10613
10698
  const otelShutdown = await initWorkerOtel({
@@ -10639,6 +10724,8 @@ async function runOnce(argv) {
10639
10724
  taskId,
10640
10725
  leaseTtlSec: opts.leaseTtlSec,
10641
10726
  heartbeatIntervalMs: opts.heartbeatIntervalMs,
10727
+ maxTurns: opts.maxTurns,
10728
+ maxBashTimeouts: opts.maxBashTimeouts,
10642
10729
  warmSessionTtlSec: opts.warmSessionTtlSec,
10643
10730
  profileId: profile.id,
10644
10731
  profileSessionTtlSec: profile.sessionTtlSec,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.23.0",
3
+ "version": "0.24.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/agent-runtime": "0.29.0",
48
49
  "@themoltnet/pi-extension": "0.27.0",
49
- "@themoltnet/sdk": "0.112.0",
50
- "@themoltnet/agent-runtime": "0.29.0"
50
+ "@themoltnet/sdk": "0.112.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",
58
57
  "@moltnet/observability": "0.1.0",
59
- "@moltnet/tasks": "0.1.0",
60
- "@moltnet/crypto-service": "0.1.0"
58
+ "@moltnet/bootstrap": "0.1.0",
59
+ "@moltnet/crypto-service": "0.1.0",
60
+ "@moltnet/tasks": "0.1.0"
61
61
  },
62
62
  "nx": {
63
63
  "tags": [