@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.
- package/README.md +15 -24
- package/dist/cli.js +63 -81
- 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
|
-
[`
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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
|
|
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.
|
|
2513
|
+
if (target.outputKind !== "artifact") errors.push({
|
|
2514
2514
|
field: "targetTaskId",
|
|
2515
|
-
message: `targetTaskId=${inp.targetTaskId}
|
|
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
|
|
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
|
|
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 —
|
|
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
|
|
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
|
-
|
|
3642
|
+
agent: await connect({ secretProviders: createNodeSecretProviderRegistry() }),
|
|
3643
|
+
credentialSource: "environment",
|
|
3644
|
+
authMechanism: "agent-key"
|
|
3667
3645
|
};
|
|
3668
3646
|
}
|
|
3669
|
-
const located =
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
4485
|
-
|
|
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":
|
|
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":
|
|
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.
|
|
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.
|
|
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/
|
|
82
|
-
"@themoltnet/
|
|
83
|
-
"@themoltnet/
|
|
84
|
-
"@themoltnet/
|
|
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/
|
|
96
|
-
"@moltnet/
|
|
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",
|