@themoltnet/agent-daemon 0.44.0 → 0.45.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 +15 -24
  2. package/dist/cli.js +63 -81
  3. package/package.json +9 -9
package/README.md CHANGED
@@ -91,7 +91,7 @@ All config flows from environment variables. The daemon reads them in
91
91
 
92
92
  For OAuth2/local mode, the agent's `moltnet.json` and gitconfig live next to
93
93
  each other in `.moltnet/<agent>/`. Provision them once via
94
- [`legreffier init`](../../docs/start/install-and-initialize.md).
94
+ [`moltnet agents init`](../../docs/start/install-and-initialize.md#initialize-an-autonomous-agent).
95
95
 
96
96
  **Auth mode.** When `MOLTNET_AGENT_KEY` is set the daemon authenticates with
97
97
  that key as an opaque bearer token (no OAuth2 exchange); otherwise it uses the
@@ -102,33 +102,24 @@ any team where the agent is authorized. It fails fast if the key is rejected,
102
102
  is not an agent, or a team binding mismatches. See
103
103
  [Run the daemon with an agent key](../../docs/operate/running-agents.md#run-the-daemon-with-an-agent-key).
104
104
 
105
- Daemon authentication and guest credential projection are two separate
106
- boundaries. How the daemon authenticates (an agent key, or OAuth2 resolved
107
- from `.moltnet/<agent>/moltnet.json` through the host secret provider) decides
108
- how the host-side SDK `Agent` is built. `--guest-credential-mode` decides
109
- whether the local agent credential tree is copied into Gondolin. Pi guests
110
- default to the `host-authenticated` boundary in **both** auth modes, regardless
111
- of local files: no `.moltnet` file, gitconfig, SSH signing key, GitHub App PEM,
112
- or MoltNet environment credential is injected into Gondolin, and mounted
113
- `.moltnet` paths are hidden. Structured MoltNet tools execute through the
114
- host-side `Agent`. Operators may explicitly opt into the legacy
115
- credential-bearing boundary with `--guest-credential-mode guest-config`; that
116
- mode requires a complete local `moltnet.json` + `env` pair and copies the
117
- complete agent configuration and signing credentials into the guest. Reserve it
118
- for local or otherwise operator-trusted tasks that genuinely need guest-shell
119
- MoltNet CLI, Git signing, or GitHub authentication. OAuth2 daemons that relied
120
- on the previous `guest-config` default for such tasks must now pass the flag
121
- explicitly.
122
-
123
- Trusted custom runtimes can deliver destination-bound HTTP credentials without
124
- switching to `guest-config`: the guest receives an opaque placeholder and the
125
- Gondolin host proxy substitutes the value only for the attested protocol,
126
- hostname pattern, and port (HTTPS/443 by default). See
105
+ Daemon authentication and the guest boundary are two separate concerns. How the
106
+ daemon authenticates (an agent key, or OAuth2 resolved from
107
+ `.moltnet/<agent>/moltnet.json` through the host secret provider) decides how the
108
+ host-side SDK `Agent` is built. The guest boundary is fixed: **the guest never
109
+ receives MoltNet credential material.** No `.moltnet` file, gitconfig, SSH
110
+ signing key, GitHub App PEM, or MoltNet environment credential is injected into
111
+ Gondolin, and mounted `.moltnet` paths are hidden. Structured MoltNet tools
112
+ execute through the host-side `Agent`.
113
+
114
+ Trusted custom runtimes can deliver destination-bound HTTP credentials to the
115
+ guest: it receives an opaque placeholder and the Gondolin host proxy substitutes
116
+ the value only for the attested protocol, hostname pattern, and port (HTTPS/443
117
+ by default). See
127
118
  [Host-brokered HTTP credentials](../../docs/operate/running-agents.md#host-brokered-http-credentials).
128
119
  This does not provide diary or Git commit signing; private-key operations remain
129
120
  host capabilities.
130
121
 
131
- Host capabilities replace the last reason for `guest-config`: the stock runtime
122
+ Host capabilities carry in-guest signing: the stock runtime
132
123
  declares `agent-signing`, so `git commit -S` and `moltnet entry create-signed`
133
124
  work inside the guest while the seed stays on the host (the daemon injects a
134
125
  signer bound to the authenticated identity and projects a non-secret gitconfig,
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, cre
12
12
  import { GuestEnvironmentBoundaryError, assertGuestEnvironmentBoundary, createPiRetryTriage, findMainWorktree, isResolvedPathInsideRoot, normalizeRetryTriageResult, redactRetryTriageSecrets } from "@themoltnet/pi-runtime";
13
13
  import { execFile, execFileSync } from "node:child_process";
14
14
  import { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from "node:fs";
15
- import { AuthenticationError, MoltNetError, connect, createExecutorAttestor, readConfig } from "@themoltnet/sdk";
15
+ import { AuthenticationError, MoltNetError, connect, createExecutorAttestor, readConfig, resolveEnvSecretReference, resolveIdentitySeed } from "@themoltnet/sdk";
16
16
  import { createNodeSecretProviderRegistry } from "@themoltnet/sdk/node";
17
17
  import { createHash, randomUUID } from "node:crypto";
18
18
  import * as ed from "@noble/ed25519";
@@ -2405,8 +2405,8 @@ async function validateJudgePackInputAsync(input, ctx) {
2405
2405
  //#endregion
2406
2406
  //#region ../../libs/tasks/src/task-types/judge-eval-attempt.ts
2407
2407
  /**
2408
- * `judge_eval_attempt` — score one completed `run_eval` attempt against a
2409
- * hidden judge rubric.
2408
+ * `judge_eval_attempt` — score one completed artifact-producing attempt
2409
+ * against a hidden judge rubric.
2410
2410
  *
2411
2411
  * output_kind: judgment
2412
2412
  * criteria: required (`successCriteria.rubric`)
@@ -2510,9 +2510,9 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
2510
2510
  field: "targetTaskId",
2511
2511
  message: `targetTaskId=${inp.targetTaskId} does not resolve to a task you can read`
2512
2512
  }];
2513
- if (target.taskType !== "run_eval") errors.push({
2513
+ if (target.outputKind !== "artifact") errors.push({
2514
2514
  field: "targetTaskId",
2515
- message: `targetTaskId=${inp.targetTaskId} is a ${target.taskType}, not a run_eval`
2515
+ message: `targetTaskId=${inp.targetTaskId} has outputKind=${target.outputKind}; only artifact-producing tasks can be judged`
2516
2516
  });
2517
2517
  if (!ctx.deferReadinessChecks && (target.status !== "completed" || target.acceptedAttemptN === null)) errors.push({
2518
2518
  field: "targetTaskId",
@@ -2524,7 +2524,7 @@ async function validateJudgeEvalAttemptInputAsync(input, ctx) {
2524
2524
  });
2525
2525
  if (!target.correlationId) errors.push({
2526
2526
  field: "targetTaskId",
2527
- message: "target run_eval has no correlation_id; cannot enforce duplicate-judge protection"
2527
+ message: "target producer has no correlation_id; cannot enforce duplicate-judge protection"
2528
2528
  });
2529
2529
  if (errors.length > 0 || !target.correlationId) return errors;
2530
2530
  const rubric = inp.successCriteria.rubric;
@@ -3387,12 +3387,6 @@ var COMMON_OPTIONAL_FLAGS = `\
3387
3387
  host git config (OAuth2) or
3388
3388
  <identityId>+<agent>[bot]@users.noreply.github.com.
3389
3389
  Env: MOLTNET_GIT_AUTHOR.
3390
- --guest-credential-mode <mode>
3391
- Guest trust boundary: host-authenticated or
3392
- guest-config. Defaults to host-authenticated for
3393
- both agent-key and OAuth2 authentication.
3394
- Explicit guest-config exposes the complete local
3395
- agent credential tree to the VM.
3396
3390
  --lease-ttl-sec <n> Sliding liveness window. Silence longer than
3397
3391
  this ends the attempt with lease_expired.
3398
3392
  Default: 300.
@@ -3439,7 +3433,8 @@ Commands:
3439
3433
  Run \`agent-daemon <command> --help\` for command-specific flags.
3440
3434
 
3441
3435
  Prerequisites:
3442
- - agent-key mode: MOLTNET_AGENT_KEY and MOLTNET_PRIVATE_KEY; no agent files
3436
+ - agent-key mode: MOLTNET_AGENT_KEY (or MOLTNET_AGENT_KEY_REF) and
3437
+ MOLTNET_PRIVATE_KEY (or MOLTNET_PRIVATE_KEY_REF); no agent files
3443
3438
  - OAuth2 and sync-sessions: <agent-root>/.moltnet/<agent>/moltnet.json
3444
3439
  - --profile — remote runtime profile supplies provider/model/sandbox
3445
3440
  policy and CWD is used as the VM mountPath.
@@ -3567,24 +3562,10 @@ function isHelpFlag(args) {
3567
3562
  //#endregion
3568
3563
  //#region src/lib/agent-context.ts
3569
3564
  /**
3570
- * Guest credentials are an explicit trust decision, never an incidental
3571
- * consequence of files found on disk. The guest boundary is independent of
3572
- * how the daemon itself authenticates: both agent-key and OAuth2 default to
3573
- * the host-authenticated boundary, where structured MoltNet operations reuse
3574
- * the trusted host-side Agent and the guest receives no credential material.
3575
- * OAuth2 still resolves that Agent from the local config and secret provider
3576
- * on the host — reading `moltnet.json` on the host does not imply projecting
3577
- * it into the guest. `guest-config` remains an explicit compatibility opt-in
3578
- * for tasks that need credential-bearing guest-shell operations.
3579
- */
3580
- function resolveDaemonGuestCredentialMode(requested) {
3581
- if (requested !== void 0 && requested !== "guest-config" && requested !== "host-authenticated") throw new Error(`Invalid --guest-credential-mode "${requested}": expected guest-config or host-authenticated.`);
3582
- return requested ?? "host-authenticated";
3583
- }
3584
- /**
3585
3565
  * Report which auth mode `connect()` will use, without ever reading the secret
3586
3566
  * value into anything logged. Agent-key mode is selected when
3587
- * `MOLTNET_AGENT_KEY` holds a non-blank value — mirroring the SDK precedence
3567
+ * `MOLTNET_AGENT_KEY` or `MOLTNET_AGENT_KEY_REF` holds a non-blank value —
3568
+ * mirroring the SDK precedence
3588
3569
  * where an environment key opts into key mode ahead of the config-file OAuth2
3589
3570
  * credentials. The daemon never passes explicit in-code credentials to
3590
3571
  * `connect()`, so this env-only check matches what `connect()` actually does.
@@ -3592,7 +3573,7 @@ function resolveDaemonGuestCredentialMode(requested) {
3592
3573
  * Pure: `env` is passed in (the config module owns the `process.env` read).
3593
3574
  */
3594
3575
  function detectAuthMode(env) {
3595
- return env.MOLTNET_AGENT_KEY?.trim() ? "agent-key" : "oauth2";
3576
+ return env.MOLTNET_AGENT_KEY?.trim() || env.MOLTNET_AGENT_KEY_REF?.trim() ? "agent-key" : "oauth2";
3596
3577
  }
3597
3578
  /**
3598
3579
  * Pure check: may the identity described by `whoami` operate the daemon as
@@ -3653,30 +3634,29 @@ async function validateStartupBinding(options) {
3653
3634
  async function resolveAgentContext(agentName, options = {}) {
3654
3635
  if (!/^[a-zA-Z0-9_-]+$/.test(agentName)) throw new Error(`Invalid agent name "${agentName}": must match /^[a-zA-Z0-9_-]+$/`);
3655
3636
  const roots = resolveCredentialRoots(options.agentRootDir);
3656
- const guestCredentialMode = resolveDaemonGuestCredentialMode(options.guestCredentialMode);
3657
3637
  if (options.authMode === "agent-key") {
3658
- const { rootDir, agentDir } = guestCredentialMode === "guest-config" ? resolveCompleteGuestCredentials(roots, agentName) : {
3659
- rootDir: roots[0] ?? process.cwd(),
3660
- agentDir: join(roots[0] ?? process.cwd(), ".moltnet", agentName)
3661
- };
3638
+ const rootDir = roots[0] ?? process.cwd();
3662
3639
  return {
3663
- agentDir,
3640
+ agentDir: join(rootDir, ".moltnet", agentName),
3664
3641
  agentRootDir: rootDir,
3665
- agent: await connect(),
3666
- guestCredentialMode
3642
+ agent: await connect({ secretProviders: createNodeSecretProviderRegistry() }),
3643
+ credentialSource: "environment",
3644
+ authMechanism: "agent-key"
3667
3645
  };
3668
3646
  }
3669
- const located = guestCredentialMode === "guest-config" ? resolveCompleteGuestCredentials(roots, agentName) : locateAgentConfig(roots, agentName);
3647
+ const located = locateAgentConfig(roots, agentName);
3670
3648
  if (located) {
3671
3649
  const agent = await connect({
3672
3650
  configDir: located.agentDir,
3673
3651
  secretProviders: createNodeSecretProviderRegistry()
3674
3652
  });
3653
+ const config = await readConfig(located.agentDir);
3675
3654
  return {
3676
3655
  agentDir: located.agentDir,
3677
3656
  agentRootDir: located.rootDir,
3678
3657
  agent,
3679
- guestCredentialMode
3658
+ credentialSource: "config",
3659
+ authMechanism: config?.agent_key_ref ? "agent-key" : "oauth2"
3680
3660
  };
3681
3661
  }
3682
3662
  const tried = roots.map((root) => join(root, ".moltnet", agentName));
@@ -3697,22 +3677,6 @@ function locateAgentConfig(roots, agentName) {
3697
3677
  };
3698
3678
  }
3699
3679
  }
3700
- function resolveCompleteGuestCredentials(roots, agentName) {
3701
- const partial = [];
3702
- for (const rootDir of roots) {
3703
- const agentDir = join(rootDir, ".moltnet", agentName);
3704
- const hasConfig = existsSync(join(agentDir, "moltnet.json"));
3705
- const hasEnv = existsSync(join(agentDir, "env"));
3706
- if (hasConfig && hasEnv) return {
3707
- rootDir,
3708
- agentDir
3709
- };
3710
- if (hasConfig || hasEnv) partial.push(agentDir);
3711
- }
3712
- if (partial.length > 0) throw new Error(`Incomplete guest credentials in ${partial.join(", ")}: moltnet.json and env must both exist for --guest-credential-mode guest-config.`);
3713
- const tried = roots.map((root) => join(root, ".moltnet", agentName));
3714
- throw new Error(`--guest-credential-mode guest-config requires an explicitly provisioned moltnet.json and env. Checked ${tried.join(", ")}.`);
3715
- }
3716
3680
  function resolveCredentialRoots(agentRootDir) {
3717
3681
  const roots = agentRootDir ? [agentRootDir] : [];
3718
3682
  try {
@@ -3734,6 +3698,8 @@ function resolveCredentialRoots(agentRootDir) {
3734
3698
  * sprinkling string lookups across the codebase.
3735
3699
  */
3736
3700
  function loadConfig() {
3701
+ assertSingleCredentialForm("MOLTNET_AGENT_KEY", "MOLTNET_AGENT_KEY_REF");
3702
+ assertSingleCredentialForm("MOLTNET_PRIVATE_KEY", "MOLTNET_PRIVATE_KEY_REF");
3737
3703
  return {
3738
3704
  otelEndpoint: process.env["MOLTNET_OTEL_ENDPOINT"] ?? "",
3739
3705
  logLevel: process.env["LOG_LEVEL"] ?? "",
@@ -3742,10 +3708,14 @@ function loadConfig() {
3742
3708
  piCodingAgentDir: process.env["PI_CODING_AGENT_DIR"] ?? "",
3743
3709
  authMode: detectAuthMode(process.env),
3744
3710
  signingPrivateKey: process.env["MOLTNET_PRIVATE_KEY"] ?? "",
3711
+ signingPrivateKeyRef: process.env["MOLTNET_PRIVATE_KEY_REF"] ?? "",
3745
3712
  gitAuthor: process.env["MOLTNET_GIT_AUTHOR"] ?? "",
3746
3713
  traceIdlePolling: readBoolean("MOLTNET_TRACE_IDLE_POLLING", process.env["MOLTNET_TRACE_IDLE_POLLING"])
3747
3714
  };
3748
3715
  }
3716
+ function assertSingleCredentialForm(valueName, refName) {
3717
+ if (process.env[valueName]?.trim() && process.env[refName]?.trim()) throw new Error(`Set only one of ${valueName} or ${refName}`);
3718
+ }
3749
3719
  function readBoolean(name, value) {
3750
3720
  if (value === void 0 || value === "") return false;
3751
3721
  if (value === "true") return true;
@@ -3953,6 +3923,7 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
3953
3923
  return {
3954
3924
  descriptor,
3955
3925
  workspaceMode,
3926
+ workspaceKind: workspaceMode === "scratch_mount" ? "scratch" : void 0,
3956
3927
  sessionKey: slotId,
3957
3928
  slotKey,
3958
3929
  slotId,
@@ -4246,15 +4217,16 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
4246
4217
  if (resolution.kind === "missing") throw new ProducerContextResolutionError(`No live producer runtime slot found for task ${targetTaskId} attempt ${targetAttemptN}`);
4247
4218
  if (resolution.kind === "no-session-path") throw new ProducerContextResolutionError(`Producer task ${targetTaskId} attempt ${targetAttemptN} has no persisted Pi session path`);
4248
4219
  if (resolution.kind === "remote-session") throw new ProducerContextResolutionError(`Producer task ${targetTaskId} attempt ${targetAttemptN} has a durable runtime session but no workspace metadata to copy`);
4220
+ const producerWorkspaceCopySource = resolveProducerWorkspaceCopySource(resolution.producerSlot, stateDirs);
4249
4221
  return {
4250
4222
  ...basePlan,
4251
4223
  workspaceMode: "scratch_mount",
4252
4224
  worktreeBranch: null,
4253
4225
  workspaceKind: "scratch",
4254
- workspaceSeed: {
4255
- copyFromPath: resolveProducerWorkspaceCopySource(resolution.producerSlot, stateDirs),
4226
+ workspaceSeed: producerWorkspaceCopySource ? {
4227
+ copyFromPath: producerWorkspaceCopySource,
4256
4228
  source: "producer"
4257
- },
4229
+ } : null,
4258
4230
  sessionPersistence: {
4259
4231
  sessionDir: `${stateDirs.piSessionsDir}/judge-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`,
4260
4232
  forkFromSessionPath: resolution.sessionPath
@@ -4297,12 +4269,18 @@ function resolveProducerWorkspaceCopySource(producer, stateDirs) {
4297
4269
  if (existsSync(workspacePath)) return workspacePath;
4298
4270
  const recoveredPath = recoverScratchWorkspacePath(producer, stateDirs);
4299
4271
  if (recoveredPath) return recoveredPath;
4272
+ if (isDisposableScratchWorkspace(producer, stateDirs)) return null;
4300
4273
  throw new ProducerContextResolutionError(`Producer workspace path is missing on disk: ${workspacePath}`);
4301
4274
  }
4302
4275
  const sharedMountRoot = dirname(dirname(stateDirs.rootDir));
4303
4276
  if (!existsSync(sharedMountRoot)) throw new ProducerContextResolutionError(`Shared producer mount root is missing on disk: ${sharedMountRoot}`);
4304
4277
  return sharedMountRoot;
4305
4278
  }
4279
+ function isDisposableScratchWorkspace(producer, stateDirs) {
4280
+ if (producer.workspace?.kind === "scratch") return true;
4281
+ if (!producer.workspace?.workspaceId) return false;
4282
+ return producer.workspace.worktreePath === join(stateDirs.rootDir, "task-workspaces", producer.workspace.workspaceId);
4283
+ }
4306
4284
  function recoverScratchWorkspacePath(producer, stateDirs) {
4307
4285
  if (producer.workspace?.worktreeBranch) return null;
4308
4286
  if (!producer.workspace?.workspaceId) return null;
@@ -4481,12 +4459,25 @@ var DAEMON_REQUIRED_SCOPES = AGENT_CREDENTIAL_SCOPES;
4481
4459
  async function resolveExecutorSigningPrivateKey(input) {
4482
4460
  if (input.authMode === "agent-key") {
4483
4461
  const privateKey = input.configuredPrivateKey.trim();
4484
- if (!privateKey) throw new Error("Agent-key daemon startup requires MOLTNET_PRIVATE_KEY containing the base64-encoded Ed25519 private key seed.");
4485
- return privateKey;
4462
+ if (privateKey) return privateKey;
4463
+ const reference = input.configuredPrivateKeyRef?.trim();
4464
+ if (!reference) throw new Error("Agent-key daemon startup requires MOLTNET_PRIVATE_KEY (or MOLTNET_PRIVATE_KEY_REF) containing the base64-encoded Ed25519 private key seed.");
4465
+ let resolved;
4466
+ try {
4467
+ resolved = await resolveEnvSecretReference(reference, createNodeSecretProviderRegistry());
4468
+ } catch (cause) {
4469
+ throw new Error(`Agent-key daemon startup could not resolve MOLTNET_PRIVATE_KEY_REF: ${cause.message}`, { cause });
4470
+ }
4471
+ if (Buffer.from(resolved, "base64").length !== 32) throw new Error("MOLTNET_PRIVATE_KEY_REF must resolve to a base64-encoded 32-byte Ed25519 seed.");
4472
+ return resolved;
4473
+ }
4474
+ const config = await readConfig(input.agentDir);
4475
+ if (!config) throw new Error(`OAuth2 daemon startup requires ${input.agentDir}/moltnet.json.`);
4476
+ try {
4477
+ return await resolveIdentitySeed(config, createNodeSecretProviderRegistry());
4478
+ } catch (cause) {
4479
+ throw new Error(`OAuth2 daemon startup could not resolve the signing seed from ${input.agentDir}/moltnet.json (keys.private_key or keys.private_key_ref): ${cause.message}`, { cause });
4486
4480
  }
4487
- const privateKey = (await readConfig(input.agentDir))?.keys?.private_key?.trim();
4488
- if (!privateKey) throw new Error(`OAuth2 daemon startup requires keys.private_key in ${input.agentDir}/moltnet.json.`);
4489
- return privateKey;
4490
4481
  }
4491
4482
  function validateDaemonScopes(whoami) {
4492
4483
  const available = new Set(whoami.scopes ?? []);
@@ -4995,7 +4986,6 @@ function commonOptionDefs() {
4995
4986
  short: "a"
4996
4987
  },
4997
4988
  "agent-root": { type: "string" },
4998
- "guest-credential-mode": { type: "string" },
4999
4989
  "git-author": { type: "string" },
5000
4990
  "lease-ttl-sec": { type: "string" },
5001
4991
  "heartbeat-interval-ms": { type: "string" },
@@ -5712,8 +5702,7 @@ async function runPolling(opts) {
5712
5702
  try {
5713
5703
  const resolvedContext = await resolveAgentContext(baseCommon.agent, {
5714
5704
  agentRootDir,
5715
- authMode: cfg.authMode,
5716
- guestCredentialMode: values["guest-credential-mode"]
5705
+ authMode: cfg.authMode
5717
5706
  });
5718
5707
  gate = "authenticate_and_bind";
5719
5708
  const whoami = await validateStartupBinding({
@@ -5724,7 +5713,8 @@ async function runPolling(opts) {
5724
5713
  const privateKey = await resolveExecutorSigningPrivateKey({
5725
5714
  authMode: cfg.authMode,
5726
5715
  agentDir: resolvedContext.agentDir,
5727
- configuredPrivateKey: cfg.signingPrivateKey
5716
+ configuredPrivateKey: cfg.signingPrivateKey,
5717
+ configuredPrivateKeyRef: cfg.signingPrivateKeyRef
5728
5718
  });
5729
5719
  gate = "validate_scopes";
5730
5720
  validateDaemonScopes(whoami);
@@ -5782,7 +5772,6 @@ async function runPolling(opts) {
5782
5772
  executables: prepared.executables
5783
5773
  });
5784
5774
  assertGuestEnvironmentBoundary({
5785
- guestCredentialMode: ctx.guestCredentialMode,
5786
5775
  forwardEnv: profile.requiredEnv,
5787
5776
  sandboxEnv: profile.sandboxConfig.env
5788
5777
  });
@@ -5860,7 +5849,7 @@ async function runPolling(opts) {
5860
5849
  resourceAttributes: {
5861
5850
  "moltnet.team.id": teamId,
5862
5851
  "moltnet.agent.name": baseCommon.agent,
5863
- "moltnet.auth.mode": cfg.authMode,
5852
+ "moltnet.auth.mode": ctx.authMechanism,
5864
5853
  "moltnet.runtime_profile.count": String(profiles.length),
5865
5854
  "moltnet.runtime_profile.ids": profiles.map((p) => p.id).join(",")
5866
5855
  }
@@ -5913,7 +5902,6 @@ async function runPolling(opts) {
5913
5902
  bindingScope: startupWhoami.credentialBinding?.bindingScope ?? null,
5914
5903
  credentialKeyId: startupWhoami.credentialBinding?.keyId ?? null,
5915
5904
  boundTeamId: startupWhoami.credentialBinding?.bindingScope === "team" ? startupWhoami.credentialBinding.boundTeamId : null,
5916
- guestCredentialMode: ctx.guestCredentialMode,
5917
5905
  taskTypes: taskTypes.length > 0 ? taskTypes : ["*"],
5918
5906
  correlationId: values["correlation-id"] ?? null,
5919
5907
  diaryIds: diaryIds.length > 0 ? diaryIds : ["*"],
@@ -6184,7 +6172,6 @@ async function runPolling(opts) {
6184
6172
  agentIdentity,
6185
6173
  hostCapabilitySigner,
6186
6174
  hostCapabilityLogger: taskLogger,
6187
- guestCredentialMode: ctx.guestCredentialMode,
6188
6175
  agentRootDir: ctx.agentRootDir,
6189
6176
  mountPath: sandbox.rootDir,
6190
6177
  provider: profile.provider,
@@ -6199,7 +6186,6 @@ async function runPolling(opts) {
6199
6186
  onVmDiagnostic: (diagnostic) => {
6200
6187
  const fields = {
6201
6188
  event: diagnostic.event,
6202
- credentialMode: diagnostic.credentialMode,
6203
6189
  ...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
6204
6190
  };
6205
6191
  if (diagnostic.level === "warning") taskLogger.warn(fields, diagnostic.message);
@@ -6353,8 +6339,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6353
6339
  try {
6354
6340
  const resolvedContext = await resolveAgentContext(initialOpts.agent, {
6355
6341
  agentRootDir,
6356
- authMode: cfg.authMode,
6357
- guestCredentialMode: values["guest-credential-mode"]
6342
+ authMode: cfg.authMode
6358
6343
  });
6359
6344
  gate = "authenticate_and_bind";
6360
6345
  const whoami = await validateStartupBinding({
@@ -6365,7 +6350,8 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6365
6350
  const privateKey = await resolveExecutorSigningPrivateKey({
6366
6351
  authMode: cfg.authMode,
6367
6352
  agentDir: resolvedContext.agentDir,
6368
- configuredPrivateKey: cfg.signingPrivateKey
6353
+ configuredPrivateKey: cfg.signingPrivateKey,
6354
+ configuredPrivateKeyRef: cfg.signingPrivateKeyRef
6369
6355
  });
6370
6356
  gate = "validate_scopes";
6371
6357
  validateDaemonScopes(whoami);
@@ -6417,7 +6403,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6417
6403
  executables: preparedRuntime.executables
6418
6404
  });
6419
6405
  assertGuestEnvironmentBoundary({
6420
- guestCredentialMode: ctx.guestCredentialMode,
6421
6406
  forwardEnv: profile.requiredEnv,
6422
6407
  sandboxEnv: profile.sandboxConfig.env
6423
6408
  });
@@ -6466,7 +6451,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6466
6451
  resourceAttributes: {
6467
6452
  "moltnet.task.id": taskId,
6468
6453
  "moltnet.agent.name": opts.agent,
6469
- "moltnet.auth.mode": cfg.authMode,
6454
+ "moltnet.auth.mode": ctx.authMechanism,
6470
6455
  "moltnet.llm.provider": profile.provider,
6471
6456
  "moltnet.llm.model": profile.model,
6472
6457
  ...profile.thinkingLevel ? { "moltnet.llm.thinking_level": profile.thinkingLevel } : {},
@@ -6505,7 +6490,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6505
6490
  profileId: profile.id,
6506
6491
  profileSessionTtlSec: profile.sessionTtlSec,
6507
6492
  profileWorkspaceTtlSec: profile.workspaceTtlSec,
6508
- guestCredentialMode: ctx.guestCredentialMode,
6509
6493
  piAgentDir: piAgentDir.path,
6510
6494
  piAgentDirSource: piAgentDir.source
6511
6495
  }, "agent-daemon.starting");
@@ -6568,7 +6552,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6568
6552
  agentIdentity,
6569
6553
  hostCapabilitySigner,
6570
6554
  hostCapabilityLogger: rootLogger,
6571
- guestCredentialMode: ctx.guestCredentialMode,
6572
6555
  agentRootDir: ctx.agentRootDir,
6573
6556
  mountPath: sandbox.rootDir,
6574
6557
  provider: profile.provider,
@@ -6583,7 +6566,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
6583
6566
  onVmDiagnostic: (diagnostic) => {
6584
6567
  const fields = {
6585
6568
  event: diagnostic.event,
6586
- credentialMode: diagnostic.credentialMode,
6587
6569
  ...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
6588
6570
  };
6589
6571
  if (diagnostic.level === "warning") rootLogger.warn(fields, diagnostic.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "description": "Universal MoltNet agent daemon host with a built-in Pi/Gondolin runtime and support for trusted operator-owned runtime modules. CLI: moltnet-agent.",
@@ -47,7 +47,7 @@
47
47
  "node": ">=24"
48
48
  },
49
49
  "dependencies": {
50
- "@earendil-works/gondolin": "^0.9.1",
50
+ "@earendil-works/gondolin": "^0.12.0",
51
51
  "@earendil-works/pi-ai": "0.79.4",
52
52
  "@earendil-works/pi-coding-agent": "0.79.4",
53
53
  "@fastify/otel": "^0.18.0",
@@ -78,10 +78,10 @@
78
78
  "pino-opentelemetry-transport": "^3.0.0",
79
79
  "pino-pretty": "^13.1.3",
80
80
  "typebox": "^1.2.8",
81
- "@themoltnet/agent-runtime": "0.44.0",
82
- "@themoltnet/os-keyring": "0.2.0",
83
- "@themoltnet/sdk": "0.137.0",
84
- "@themoltnet/pi-runtime": "0.12.0"
81
+ "@themoltnet/os-keyring": "0.3.0",
82
+ "@themoltnet/agent-runtime": "0.45.0",
83
+ "@themoltnet/pi-runtime": "0.12.2",
84
+ "@themoltnet/sdk": "0.138.0"
85
85
  },
86
86
  "devDependencies": {
87
87
  "tsx": "^4.7.0",
@@ -89,12 +89,12 @@
89
89
  "vite": "^8.0.0",
90
90
  "vite-plugin-dts": "^4.5.4",
91
91
  "vitest": "^3.0.0",
92
+ "@moltnet/bootstrap": "0.1.0",
92
93
  "@moltnet/crypto-service": "0.1.0",
93
94
  "@moltnet/models": "0.1.0",
94
95
  "@moltnet/observability": "0.1.0",
95
- "@moltnet/tasks": "0.1.0",
96
- "@moltnet/bootstrap": "0.1.0",
97
- "@moltnet/runtime-profiles": "0.1.0"
96
+ "@moltnet/runtime-profiles": "0.1.0",
97
+ "@moltnet/tasks": "0.1.0"
98
98
  },
99
99
  "nx": {
100
100
  "projectType": "application",