@themoltnet/agent-daemon 0.43.0 → 0.44.1
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 +23 -22
- package/dist/cli.js +124 -89
- package/dist/pi.d.ts.map +1 -1
- package/dist/pi.js +3 -2
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -102,32 +102,33 @@ 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
|
|
|
122
|
+
Host capabilities carry in-guest signing: the stock runtime
|
|
123
|
+
declares `agent-signing`, so `git commit -S` and `moltnet entry create-signed`
|
|
124
|
+
work inside the guest while the seed stays on the host (the daemon injects a
|
|
125
|
+
signer bound to the authenticated identity and projects a non-secret gitconfig,
|
|
126
|
+
an `SSH_AUTH_SOCK` service, and `MOLTNET_SIGNER_URL`). Grant
|
|
127
|
+
`capability:agent-signing` (or per-operation `capability:agent-signing:<op>`)
|
|
128
|
+
in the tool policy. `--git-author "Name <email>"` / `MOLTNET_GIT_AUTHOR`
|
|
129
|
+
overrides the projected git identity. See
|
|
130
|
+
[Host capabilities](../../docs/operate/running-agents.md#host-capabilities).
|
|
131
|
+
|
|
131
132
|
`sync-sessions` does not prepare or attest executors, so it remains independent
|
|
132
133
|
of `MOLTNET_PRIVATE_KEY`.
|
|
133
134
|
|
package/dist/cli.js
CHANGED
|
@@ -8,20 +8,20 @@ import "multiformats/hashes/sha2";
|
|
|
8
8
|
import "typebox/value";
|
|
9
9
|
import { dirname, join, resolve } from "node:path";
|
|
10
10
|
import { parseArgs, promisify } from "node:util";
|
|
11
|
-
import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, resolveProfileWarmSessionTtlSec, resolveRuntimeProfile, resolveRuntimeProfiles, validateRuntimeProfilePrerequisites } from "@themoltnet/agent-runtime";
|
|
11
|
+
import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, createLocalSeedSigner, resolveAgentIdentity, resolveProfileWarmSessionTtlSec, resolveRuntimeProfile, resolveRuntimeProfiles, validateRuntimeProfilePrerequisites } from "@themoltnet/agent-runtime";
|
|
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
15
|
import { AuthenticationError, MoltNetError, connect, createExecutorAttestor, readConfig } from "@themoltnet/sdk";
|
|
16
16
|
import { createNodeSecretProviderRegistry } from "@themoltnet/sdk/node";
|
|
17
17
|
import { createHash, randomUUID } from "node:crypto";
|
|
18
|
+
import * as ed from "@noble/ed25519";
|
|
19
|
+
import { createHash as createHash$1, randomBytes } from "crypto";
|
|
18
20
|
import "multiformats/codecs/raw";
|
|
19
21
|
import "multiformats/hashes/digest";
|
|
20
22
|
import "@noble/hashes/sha2";
|
|
21
23
|
import "multiformats/bases/base32";
|
|
22
24
|
import { ed25519 } from "@noble/curves/ed25519.js";
|
|
23
|
-
import * as ed from "@noble/ed25519";
|
|
24
|
-
import { createHash as createHash$1, randomBytes } from "crypto";
|
|
25
25
|
import "@ipld/dag-cbor";
|
|
26
26
|
import "@noble/ciphers/chacha";
|
|
27
27
|
import "@noble/hashes/hkdf";
|
|
@@ -690,12 +690,12 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
690
690
|
},
|
|
691
691
|
"accountable-delivery-v1": {
|
|
692
692
|
binding: "prompt_prefix",
|
|
693
|
-
content: "# Accountable delivery\n\n- Pair every commit made during this task with a task-provenance diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer. The tool does not currently promise a content signature; never describe an entry as signed
|
|
693
|
+
content: "# Accountable delivery\n\n- Pair every commit made during this task with a task-provenance diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer. The tool does not currently promise a content signature unless you pass `signed: true` while the runtime kernel declares the `agent-signing` host capability; never describe an entry as signed otherwise.\n- When the runtime kernel declares `agent-signing`, sign commits normally with `git commit -S`: the signature is brokered to the trusted host through `SSH_AUTH_SOCK` and no private key exists in the guest. Without that capability commits are unsigned; do not disable signing the runtime provides, and never try to obtain a key from host configuration.\n- Push a branch and open or update a pull request only when the task asks for it. Use a host-brokered GitHub placeholder only when the runtime kernel declares one; if no GitHub credential is active, the authenticated operation is unavailable.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
|
|
694
694
|
slug: "accountable-delivery-v1"
|
|
695
695
|
},
|
|
696
696
|
"judgment-diary-v1": {
|
|
697
697
|
binding: "prompt_prefix",
|
|
698
|
-
content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a diary entry with the `moltnet_create_entry` custom tool before submitting the structured judgment. Capture the rationale and evidence that support the verdict. Do not claim a content signature unless the
|
|
698
|
+
content: "# Judgment diary discipline\n\n- For an `assess_brief`, `judge_pack`, or `pr_review` task, create a diary entry with the `moltnet_create_entry` custom tool before submitting the structured judgment. Capture the rationale and evidence that support the verdict. Do not claim a content signature unless you created the entry with `signed: true` under a runtime that declares the `agent-signing` host capability.\n- Add the `judgment` tag and the active task type tag (`assess_brief`, `judge_pack`, or `pr_review`). For `judge_pack`, also add `rubric:<rubricId>` from the task facts.\n- Do not use a shell `moltnet entry` command: task provenance is injected only by the custom tool.",
|
|
699
699
|
slug: "judgment-diary-v1"
|
|
700
700
|
},
|
|
701
701
|
"proactive-memory-v1": {
|
|
@@ -710,7 +710,7 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
710
710
|
},
|
|
711
711
|
"task-diary-discipline-v1": {
|
|
712
712
|
binding: "prompt_prefix",
|
|
713
|
-
content: "# Task diary discipline\n\n- During a daemon task, create diary entries only through the `moltnet_create_entry` custom tool. It binds entries to the current task diary and injects task, type, attempt, and correlation provenance tags.\n- Do not shell out to `moltnet entry create`, `moltnet entry create-signed`, or any other `moltnet entry` subcommand from bash while a task is running. Those paths bypass the custom tool's task-tag injection, so task-filtered diary queries cannot find the entry.\n- You may add useful tags, but do not try to replace task provenance supplied by the runtime.",
|
|
713
|
+
content: "# Task diary discipline\n\n- During a daemon task, create diary entries only through the `moltnet_create_entry` custom tool. It binds entries to the current task diary and injects task, type, attempt, and correlation provenance tags.\n- Do not shell out to `moltnet entry create`, `moltnet entry create-signed`, or any other `moltnet entry` subcommand from bash while a task is running. For a content-signed entry pass `signed: true` to the custom tool instead; it signs on the trusted host. Those shell paths bypass the custom tool's task-tag injection, so task-filtered diary queries cannot find the entry.\n- You may add useful tags, but do not try to replace task provenance supplied by the runtime.",
|
|
714
714
|
slug: "task-diary-discipline-v1"
|
|
715
715
|
},
|
|
716
716
|
"verification-and-artifacts-v1": {
|
|
@@ -1132,7 +1132,11 @@ Type.Object({ name: Type.String({
|
|
|
1132
1132
|
maxLength: 255
|
|
1133
1133
|
}) });
|
|
1134
1134
|
Type.Object({
|
|
1135
|
-
role: Type.Optional(Type.Union([
|
|
1135
|
+
role: Type.Optional(Type.Union([
|
|
1136
|
+
Type.Literal("manager"),
|
|
1137
|
+
Type.Literal("executor"),
|
|
1138
|
+
Type.Literal("member")
|
|
1139
|
+
])),
|
|
1136
1140
|
maxUses: Type.Optional(Type.Integer({
|
|
1137
1141
|
minimum: 1,
|
|
1138
1142
|
default: 1
|
|
@@ -1144,10 +1148,15 @@ Type.Object({
|
|
|
1144
1148
|
}))
|
|
1145
1149
|
});
|
|
1146
1150
|
Type.Object({ code: Type.String({ minLength: 1 }) });
|
|
1147
|
-
Type.Object({ role: Type.Union([
|
|
1151
|
+
Type.Object({ role: Type.Union([
|
|
1152
|
+
Type.Literal("manager"),
|
|
1153
|
+
Type.Literal("executor"),
|
|
1154
|
+
Type.Literal("member")
|
|
1155
|
+
]) });
|
|
1148
1156
|
var TeamRoleSchema = Type.Union([
|
|
1149
1157
|
Type.Literal("owner"),
|
|
1150
1158
|
Type.Literal("manager"),
|
|
1159
|
+
Type.Literal("executor"),
|
|
1151
1160
|
Type.Literal("member")
|
|
1152
1161
|
]);
|
|
1153
1162
|
Type.Object({
|
|
@@ -1158,7 +1167,11 @@ var DateTimeUnsafe = Type.Unsafe(Type.String({ format: "date-time" }));
|
|
|
1158
1167
|
Type.Object({
|
|
1159
1168
|
id: UuidSchema,
|
|
1160
1169
|
code: Type.String(),
|
|
1161
|
-
role: Type.Union([
|
|
1170
|
+
role: Type.Union([
|
|
1171
|
+
Type.Literal("manager"),
|
|
1172
|
+
Type.Literal("executor"),
|
|
1173
|
+
Type.Literal("member")
|
|
1174
|
+
]),
|
|
1162
1175
|
maxUses: Type.Integer(),
|
|
1163
1176
|
useCount: Type.Integer(),
|
|
1164
1177
|
expiresAt: DateTimeUnsafe,
|
|
@@ -1190,11 +1203,19 @@ Type.Object({
|
|
|
1190
1203
|
});
|
|
1191
1204
|
Type.Object({
|
|
1192
1205
|
teamId: UuidSchema,
|
|
1193
|
-
role: Type.Union([
|
|
1206
|
+
role: Type.Union([
|
|
1207
|
+
Type.Literal("manager"),
|
|
1208
|
+
Type.Literal("executor"),
|
|
1209
|
+
Type.Literal("member")
|
|
1210
|
+
])
|
|
1194
1211
|
});
|
|
1195
1212
|
Type.Object({
|
|
1196
1213
|
updated: Type.Boolean(),
|
|
1197
|
-
role: Type.Union([
|
|
1214
|
+
role: Type.Union([
|
|
1215
|
+
Type.Literal("manager"),
|
|
1216
|
+
Type.Literal("executor"),
|
|
1217
|
+
Type.Literal("member")
|
|
1218
|
+
])
|
|
1198
1219
|
});
|
|
1199
1220
|
Type.Object({ deleted: Type.Boolean() });
|
|
1200
1221
|
Type.Object({ removed: Type.Boolean() });
|
|
@@ -1204,6 +1225,7 @@ var FoundingMemberSchema = Type.Object({
|
|
|
1204
1225
|
role: Type.Union([
|
|
1205
1226
|
Type.Literal("owner"),
|
|
1206
1227
|
Type.Literal("manager"),
|
|
1228
|
+
Type.Literal("executor"),
|
|
1207
1229
|
Type.Literal("member")
|
|
1208
1230
|
])
|
|
1209
1231
|
});
|
|
@@ -3359,12 +3381,12 @@ var COMMON_OPTIONAL_FLAGS = `\
|
|
|
3359
3381
|
sandbox policy.
|
|
3360
3382
|
--agent-root <path> Directory that owns .moltnet/<agent>. Default:
|
|
3361
3383
|
CWD, with git root fallback when available.
|
|
3362
|
-
--
|
|
3363
|
-
|
|
3364
|
-
guest
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3384
|
+
--git-author <"Name <email>">
|
|
3385
|
+
Non-secret git identity projected into the
|
|
3386
|
+
guest for host-brokered commit signing. Default:
|
|
3387
|
+
host git config (OAuth2) or
|
|
3388
|
+
<identityId>+<agent>[bot]@users.noreply.github.com.
|
|
3389
|
+
Env: MOLTNET_GIT_AUTHOR.
|
|
3368
3390
|
--lease-ttl-sec <n> Sliding liveness window. Silence longer than
|
|
3369
3391
|
this ends the attempt with lease_expired.
|
|
3370
3392
|
Default: 300.
|
|
@@ -3539,21 +3561,6 @@ function isHelpFlag(args) {
|
|
|
3539
3561
|
//#endregion
|
|
3540
3562
|
//#region src/lib/agent-context.ts
|
|
3541
3563
|
/**
|
|
3542
|
-
* Guest credentials are an explicit trust decision, never an incidental
|
|
3543
|
-
* consequence of files found on disk. The guest boundary is independent of
|
|
3544
|
-
* how the daemon itself authenticates: both agent-key and OAuth2 default to
|
|
3545
|
-
* the host-authenticated boundary, where structured MoltNet operations reuse
|
|
3546
|
-
* the trusted host-side Agent and the guest receives no credential material.
|
|
3547
|
-
* OAuth2 still resolves that Agent from the local config and secret provider
|
|
3548
|
-
* on the host — reading `moltnet.json` on the host does not imply projecting
|
|
3549
|
-
* it into the guest. `guest-config` remains an explicit compatibility opt-in
|
|
3550
|
-
* for tasks that need credential-bearing guest-shell operations.
|
|
3551
|
-
*/
|
|
3552
|
-
function resolveDaemonGuestCredentialMode(requested) {
|
|
3553
|
-
if (requested !== void 0 && requested !== "guest-config" && requested !== "host-authenticated") throw new Error(`Invalid --guest-credential-mode "${requested}": expected guest-config or host-authenticated.`);
|
|
3554
|
-
return requested ?? "host-authenticated";
|
|
3555
|
-
}
|
|
3556
|
-
/**
|
|
3557
3564
|
* Report which auth mode `connect()` will use, without ever reading the secret
|
|
3558
3565
|
* value into anything logged. Agent-key mode is selected when
|
|
3559
3566
|
* `MOLTNET_AGENT_KEY` holds a non-blank value — mirroring the SDK precedence
|
|
@@ -3625,20 +3632,15 @@ async function validateStartupBinding(options) {
|
|
|
3625
3632
|
async function resolveAgentContext(agentName, options = {}) {
|
|
3626
3633
|
if (!/^[a-zA-Z0-9_-]+$/.test(agentName)) throw new Error(`Invalid agent name "${agentName}": must match /^[a-zA-Z0-9_-]+$/`);
|
|
3627
3634
|
const roots = resolveCredentialRoots(options.agentRootDir);
|
|
3628
|
-
const guestCredentialMode = resolveDaemonGuestCredentialMode(options.guestCredentialMode);
|
|
3629
3635
|
if (options.authMode === "agent-key") {
|
|
3630
|
-
const
|
|
3631
|
-
rootDir: roots[0] ?? process.cwd(),
|
|
3632
|
-
agentDir: join(roots[0] ?? process.cwd(), ".moltnet", agentName)
|
|
3633
|
-
};
|
|
3636
|
+
const rootDir = roots[0] ?? process.cwd();
|
|
3634
3637
|
return {
|
|
3635
|
-
agentDir,
|
|
3638
|
+
agentDir: join(rootDir, ".moltnet", agentName),
|
|
3636
3639
|
agentRootDir: rootDir,
|
|
3637
|
-
agent: await connect()
|
|
3638
|
-
guestCredentialMode
|
|
3640
|
+
agent: await connect()
|
|
3639
3641
|
};
|
|
3640
3642
|
}
|
|
3641
|
-
const located =
|
|
3643
|
+
const located = locateAgentConfig(roots, agentName);
|
|
3642
3644
|
if (located) {
|
|
3643
3645
|
const agent = await connect({
|
|
3644
3646
|
configDir: located.agentDir,
|
|
@@ -3647,8 +3649,7 @@ async function resolveAgentContext(agentName, options = {}) {
|
|
|
3647
3649
|
return {
|
|
3648
3650
|
agentDir: located.agentDir,
|
|
3649
3651
|
agentRootDir: located.rootDir,
|
|
3650
|
-
agent
|
|
3651
|
-
guestCredentialMode
|
|
3652
|
+
agent
|
|
3652
3653
|
};
|
|
3653
3654
|
}
|
|
3654
3655
|
const tried = roots.map((root) => join(root, ".moltnet", agentName));
|
|
@@ -3669,22 +3670,6 @@ function locateAgentConfig(roots, agentName) {
|
|
|
3669
3670
|
};
|
|
3670
3671
|
}
|
|
3671
3672
|
}
|
|
3672
|
-
function resolveCompleteGuestCredentials(roots, agentName) {
|
|
3673
|
-
const partial = [];
|
|
3674
|
-
for (const rootDir of roots) {
|
|
3675
|
-
const agentDir = join(rootDir, ".moltnet", agentName);
|
|
3676
|
-
const hasConfig = existsSync(join(agentDir, "moltnet.json"));
|
|
3677
|
-
const hasEnv = existsSync(join(agentDir, "env"));
|
|
3678
|
-
if (hasConfig && hasEnv) return {
|
|
3679
|
-
rootDir,
|
|
3680
|
-
agentDir
|
|
3681
|
-
};
|
|
3682
|
-
if (hasConfig || hasEnv) partial.push(agentDir);
|
|
3683
|
-
}
|
|
3684
|
-
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.`);
|
|
3685
|
-
const tried = roots.map((root) => join(root, ".moltnet", agentName));
|
|
3686
|
-
throw new Error(`--guest-credential-mode guest-config requires an explicitly provisioned moltnet.json and env. Checked ${tried.join(", ")}.`);
|
|
3687
|
-
}
|
|
3688
3673
|
function resolveCredentialRoots(agentRootDir) {
|
|
3689
3674
|
const roots = agentRootDir ? [agentRootDir] : [];
|
|
3690
3675
|
try {
|
|
@@ -3714,6 +3699,7 @@ function loadConfig() {
|
|
|
3714
3699
|
piCodingAgentDir: process.env["PI_CODING_AGENT_DIR"] ?? "",
|
|
3715
3700
|
authMode: detectAuthMode(process.env),
|
|
3716
3701
|
signingPrivateKey: process.env["MOLTNET_PRIVATE_KEY"] ?? "",
|
|
3702
|
+
gitAuthor: process.env["MOLTNET_GIT_AUTHOR"] ?? "",
|
|
3717
3703
|
traceIdlePolling: readBoolean("MOLTNET_TRACE_IDLE_POLLING", process.env["MOLTNET_TRACE_IDLE_POLLING"])
|
|
3718
3704
|
};
|
|
3719
3705
|
}
|
|
@@ -3738,6 +3724,30 @@ async function abortActiveAttemptOnSignal(opts) {
|
|
|
3738
3724
|
}
|
|
3739
3725
|
}
|
|
3740
3726
|
//#endregion
|
|
3727
|
+
//#region src/lib/agent-identity.ts
|
|
3728
|
+
/**
|
|
3729
|
+
* Build the non-secret identity projected into guests. Host git config is a
|
|
3730
|
+
* non-secret input and is consulted only on OAuth2 hosts, which already read
|
|
3731
|
+
* that configuration for the signing seed; configless agent-key hosts never
|
|
3732
|
+
* touch a config directory.
|
|
3733
|
+
*/
|
|
3734
|
+
async function resolveDaemonAgentIdentity(input) {
|
|
3735
|
+
let hostGit;
|
|
3736
|
+
if (input.gitAuthor === void 0 && input.authMode === "oauth2") {
|
|
3737
|
+
const config = await readConfig(input.agentDir);
|
|
3738
|
+
hostGit = config?.git ? {
|
|
3739
|
+
name: config.git.name,
|
|
3740
|
+
email: config.git.email
|
|
3741
|
+
} : void 0;
|
|
3742
|
+
}
|
|
3743
|
+
return resolveAgentIdentity({
|
|
3744
|
+
agentName: input.agentName,
|
|
3745
|
+
whoami: input.whoami,
|
|
3746
|
+
gitAuthor: input.gitAuthor,
|
|
3747
|
+
hostGit
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
//#endregion
|
|
3741
3751
|
//#region src/lib/correlation.ts
|
|
3742
3752
|
var execFileAsync = promisify(execFile);
|
|
3743
3753
|
var CORRELATION_TRAILER_KEY = "Moltnet-Correlation-Id";
|
|
@@ -4256,6 +4266,19 @@ function recoverScratchWorkspacePath(producer, stateDirs) {
|
|
|
4256
4266
|
const fallback = join(stateDirs.rootDir, "task-workspaces", producer.workspace.workspaceId);
|
|
4257
4267
|
return existsSync(fallback) ? fallback : null;
|
|
4258
4268
|
}
|
|
4269
|
+
//#endregion
|
|
4270
|
+
//#region ../../libs/crypto-service/src/ssh.ts
|
|
4271
|
+
/**
|
|
4272
|
+
* SSH key format conversion for MoltNet Ed25519 keys
|
|
4273
|
+
*
|
|
4274
|
+
* Converts MoltNet agent keys (ed25519:<base64>) to OpenSSH format
|
|
4275
|
+
* for use with git commit signing and SSH authentication.
|
|
4276
|
+
*/
|
|
4277
|
+
if (!ed.etc.sha512Sync) ed.etc.sha512Sync = (...m) => {
|
|
4278
|
+
const hash = createHash$1("sha512");
|
|
4279
|
+
m.forEach((msg) => hash.update(msg));
|
|
4280
|
+
return hash.digest();
|
|
4281
|
+
};
|
|
4259
4282
|
new TextEncoder();
|
|
4260
4283
|
//#endregion
|
|
4261
4284
|
//#region ../../libs/crypto-service/src/crypto.service.ts
|
|
@@ -4408,19 +4431,7 @@ ed.etc.sha512Sync = (...m) => {
|
|
|
4408
4431
|
m.forEach((msg) => hash.update(msg));
|
|
4409
4432
|
return hash.digest();
|
|
4410
4433
|
};
|
|
4411
|
-
|
|
4412
|
-
//#region ../../libs/crypto-service/src/ssh.ts
|
|
4413
|
-
/**
|
|
4414
|
-
* SSH key format conversion for MoltNet Ed25519 keys
|
|
4415
|
-
*
|
|
4416
|
-
* Converts MoltNet agent keys (ed25519:<base64>) to OpenSSH format
|
|
4417
|
-
* for use with git commit signing and SSH authentication.
|
|
4418
|
-
*/
|
|
4419
|
-
if (!ed.etc.sha512Sync) ed.etc.sha512Sync = (...m) => {
|
|
4420
|
-
const hash = createHash$1("sha512");
|
|
4421
|
-
m.forEach((msg) => hash.update(msg));
|
|
4422
|
-
return hash.digest();
|
|
4423
|
-
};
|
|
4434
|
+
new TextEncoder().encode("SSHSIG");
|
|
4424
4435
|
//#endregion
|
|
4425
4436
|
//#region src/lib/executor-attestation.ts
|
|
4426
4437
|
var DAEMON_REQUIRED_SCOPES = AGENT_CREDENTIAL_SCOPES;
|
|
@@ -4941,7 +4952,7 @@ function commonOptionDefs() {
|
|
|
4941
4952
|
short: "a"
|
|
4942
4953
|
},
|
|
4943
4954
|
"agent-root": { type: "string" },
|
|
4944
|
-
"
|
|
4955
|
+
"git-author": { type: "string" },
|
|
4945
4956
|
"lease-ttl-sec": { type: "string" },
|
|
4946
4957
|
"heartbeat-interval-ms": { type: "string" },
|
|
4947
4958
|
"max-batch-size": { type: "string" },
|
|
@@ -5652,13 +5663,12 @@ async function runPolling(opts) {
|
|
|
5652
5663
|
if (taskTypes.length === 0) console.error(`[${opts.modeLabel}] --task-types is empty — daemon will accept any registered type. Pass an explicit list to limit scope (e.g. --task-types fulfill_brief).`);
|
|
5653
5664
|
const cfg = loadConfig();
|
|
5654
5665
|
const agentRootDir = resolve(process.cwd(), values["agent-root"] ?? process.cwd());
|
|
5655
|
-
const { ctx, signingPrivateKey, startupWhoami } = await (async () => {
|
|
5666
|
+
const { ctx, signingPrivateKey, startupWhoami, agentIdentity, hostCapabilitySigner } = await (async () => {
|
|
5656
5667
|
let gate = "resolve_agent_context";
|
|
5657
5668
|
try {
|
|
5658
5669
|
const resolvedContext = await resolveAgentContext(baseCommon.agent, {
|
|
5659
5670
|
agentRootDir,
|
|
5660
|
-
authMode: cfg.authMode
|
|
5661
|
-
guestCredentialMode: values["guest-credential-mode"]
|
|
5671
|
+
authMode: cfg.authMode
|
|
5662
5672
|
});
|
|
5663
5673
|
gate = "authenticate_and_bind";
|
|
5664
5674
|
const whoami = await validateStartupBinding({
|
|
@@ -5678,10 +5688,24 @@ async function runPolling(opts) {
|
|
|
5678
5688
|
whoami,
|
|
5679
5689
|
signingPrivateKey: privateKey
|
|
5680
5690
|
});
|
|
5691
|
+
gate = "resolve_agent_identity";
|
|
5692
|
+
const agentIdentity = await resolveDaemonAgentIdentity({
|
|
5693
|
+
agentName: baseCommon.agent,
|
|
5694
|
+
whoami,
|
|
5695
|
+
authMode: cfg.authMode,
|
|
5696
|
+
agentDir: resolvedContext.agentDir,
|
|
5697
|
+
gitAuthor: values["git-author"] ?? (cfg.gitAuthor || void 0)
|
|
5698
|
+
});
|
|
5681
5699
|
return {
|
|
5682
5700
|
ctx: resolvedContext,
|
|
5683
5701
|
signingPrivateKey: privateKey,
|
|
5684
|
-
startupWhoami: whoami
|
|
5702
|
+
startupWhoami: whoami,
|
|
5703
|
+
agentIdentity,
|
|
5704
|
+
hostCapabilitySigner: createLocalSeedSigner({
|
|
5705
|
+
privateKeySeed: privateKey,
|
|
5706
|
+
agent: resolvedContext.agent,
|
|
5707
|
+
identity: agentIdentity
|
|
5708
|
+
})
|
|
5685
5709
|
};
|
|
5686
5710
|
} catch (error) {
|
|
5687
5711
|
await logDaemonStartupFailure({
|
|
@@ -5713,7 +5737,6 @@ async function runPolling(opts) {
|
|
|
5713
5737
|
executables: prepared.executables
|
|
5714
5738
|
});
|
|
5715
5739
|
assertGuestEnvironmentBoundary({
|
|
5716
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
5717
5740
|
forwardEnv: profile.requiredEnv,
|
|
5718
5741
|
sandboxEnv: profile.sandboxConfig.env
|
|
5719
5742
|
});
|
|
@@ -5844,7 +5867,6 @@ async function runPolling(opts) {
|
|
|
5844
5867
|
bindingScope: startupWhoami.credentialBinding?.bindingScope ?? null,
|
|
5845
5868
|
credentialKeyId: startupWhoami.credentialBinding?.keyId ?? null,
|
|
5846
5869
|
boundTeamId: startupWhoami.credentialBinding?.bindingScope === "team" ? startupWhoami.credentialBinding.boundTeamId : null,
|
|
5847
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
5848
5870
|
taskTypes: taskTypes.length > 0 ? taskTypes : ["*"],
|
|
5849
5871
|
correlationId: values["correlation-id"] ?? null,
|
|
5850
5872
|
diaryIds: diaryIds.length > 0 ? diaryIds : ["*"],
|
|
@@ -6112,7 +6134,9 @@ async function runPolling(opts) {
|
|
|
6112
6134
|
const rawExecuteTask = selected.preparedRuntime.createTaskExecutor({
|
|
6113
6135
|
agentName: common.agent,
|
|
6114
6136
|
moltnetAgent: ctx.agent,
|
|
6115
|
-
|
|
6137
|
+
agentIdentity,
|
|
6138
|
+
hostCapabilitySigner,
|
|
6139
|
+
hostCapabilityLogger: taskLogger,
|
|
6116
6140
|
agentRootDir: ctx.agentRootDir,
|
|
6117
6141
|
mountPath: sandbox.rootDir,
|
|
6118
6142
|
provider: profile.provider,
|
|
@@ -6127,7 +6151,6 @@ async function runPolling(opts) {
|
|
|
6127
6151
|
onVmDiagnostic: (diagnostic) => {
|
|
6128
6152
|
const fields = {
|
|
6129
6153
|
event: diagnostic.event,
|
|
6130
|
-
credentialMode: diagnostic.credentialMode,
|
|
6131
6154
|
...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
|
|
6132
6155
|
};
|
|
6133
6156
|
if (diagnostic.level === "warning") taskLogger.warn(fields, diagnostic.message);
|
|
@@ -6276,13 +6299,12 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6276
6299
|
const cfg = loadConfig();
|
|
6277
6300
|
const initialOpts = opts;
|
|
6278
6301
|
const agentRootDir = resolve(process.cwd(), values["agent-root"] ?? process.cwd());
|
|
6279
|
-
const { ctx, signingPrivateKey } = await (async () => {
|
|
6302
|
+
const { ctx, signingPrivateKey, agentIdentity, hostCapabilitySigner } = await (async () => {
|
|
6280
6303
|
let gate = "resolve_agent_context";
|
|
6281
6304
|
try {
|
|
6282
6305
|
const resolvedContext = await resolveAgentContext(initialOpts.agent, {
|
|
6283
6306
|
agentRootDir,
|
|
6284
|
-
authMode: cfg.authMode
|
|
6285
|
-
guestCredentialMode: values["guest-credential-mode"]
|
|
6307
|
+
authMode: cfg.authMode
|
|
6286
6308
|
});
|
|
6287
6309
|
gate = "authenticate_and_bind";
|
|
6288
6310
|
const whoami = await validateStartupBinding({
|
|
@@ -6302,9 +6324,23 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6302
6324
|
whoami,
|
|
6303
6325
|
signingPrivateKey: privateKey
|
|
6304
6326
|
});
|
|
6327
|
+
gate = "resolve_agent_identity";
|
|
6328
|
+
const agentIdentity = await resolveDaemonAgentIdentity({
|
|
6329
|
+
agentName: initialOpts.agent,
|
|
6330
|
+
whoami,
|
|
6331
|
+
authMode: cfg.authMode,
|
|
6332
|
+
agentDir: resolvedContext.agentDir,
|
|
6333
|
+
gitAuthor: values["git-author"] ?? (cfg.gitAuthor || void 0)
|
|
6334
|
+
});
|
|
6305
6335
|
return {
|
|
6306
6336
|
ctx: resolvedContext,
|
|
6307
|
-
signingPrivateKey: privateKey
|
|
6337
|
+
signingPrivateKey: privateKey,
|
|
6338
|
+
agentIdentity,
|
|
6339
|
+
hostCapabilitySigner: createLocalSeedSigner({
|
|
6340
|
+
privateKeySeed: privateKey,
|
|
6341
|
+
agent: resolvedContext.agent,
|
|
6342
|
+
identity: agentIdentity
|
|
6343
|
+
})
|
|
6308
6344
|
};
|
|
6309
6345
|
} catch (error) {
|
|
6310
6346
|
await logDaemonStartupFailure({
|
|
@@ -6331,7 +6367,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6331
6367
|
executables: preparedRuntime.executables
|
|
6332
6368
|
});
|
|
6333
6369
|
assertGuestEnvironmentBoundary({
|
|
6334
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6335
6370
|
forwardEnv: profile.requiredEnv,
|
|
6336
6371
|
sandboxEnv: profile.sandboxConfig.env
|
|
6337
6372
|
});
|
|
@@ -6419,7 +6454,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6419
6454
|
profileId: profile.id,
|
|
6420
6455
|
profileSessionTtlSec: profile.sessionTtlSec,
|
|
6421
6456
|
profileWorkspaceTtlSec: profile.workspaceTtlSec,
|
|
6422
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6423
6457
|
piAgentDir: piAgentDir.path,
|
|
6424
6458
|
piAgentDirSource: piAgentDir.source
|
|
6425
6459
|
}, "agent-daemon.starting");
|
|
@@ -6479,7 +6513,9 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6479
6513
|
const rawExecuteTask = preparedRuntime.createTaskExecutor({
|
|
6480
6514
|
agentName: opts.agent,
|
|
6481
6515
|
moltnetAgent: ctx.agent,
|
|
6482
|
-
|
|
6516
|
+
agentIdentity,
|
|
6517
|
+
hostCapabilitySigner,
|
|
6518
|
+
hostCapabilityLogger: rootLogger,
|
|
6483
6519
|
agentRootDir: ctx.agentRootDir,
|
|
6484
6520
|
mountPath: sandbox.rootDir,
|
|
6485
6521
|
provider: profile.provider,
|
|
@@ -6494,7 +6530,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6494
6530
|
onVmDiagnostic: (diagnostic) => {
|
|
6495
6531
|
const fields = {
|
|
6496
6532
|
event: diagnostic.event,
|
|
6497
|
-
credentialMode: diagnostic.credentialMode,
|
|
6498
6533
|
...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
|
|
6499
6534
|
};
|
|
6500
6535
|
if (diagnostic.level === "warning") rootLogger.warn(fields, diagnostic.message);
|
package/dist/pi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,cAAc,CAAC;AAEhF,eAAO,MAAM,oBAAoB,kkBAIvB,CAAC;AAEX,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,GAC3B,oBAAoB,CA4DtB;AAED,eAAO,MAAM,0BAA0B,qBASrC,CAAC;AAEH,eAAO,MAAM,sBAAsB,sBAElC,CAAC"}
|
package/dist/pi.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, MOLTNET_TOOL_NAMES, buildPiExecutorManifest, createPiTaskExecutor, defineGondolinTemplate, definePiRuntime } from "@themoltnet/pi-runtime";
|
|
2
|
+
import { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, MOLTNET_TOOL_NAMES, agentSigningCapability, buildPiExecutorManifest, createPiTaskExecutor, defineGondolinTemplate, definePiRuntime } from "@themoltnet/pi-runtime";
|
|
3
3
|
//#region src/pi.ts
|
|
4
4
|
var PI_KERNEL_TOOL_NAMES = [
|
|
5
5
|
...GONDOLIN_TOOL_NAMES,
|
|
@@ -59,7 +59,8 @@ var defaultPiRuntimeDefinition = definePiRuntime({
|
|
|
59
59
|
id: "moltnet-default-gondolin",
|
|
60
60
|
version: "1",
|
|
61
61
|
executables: GONDOLIN_BASE_EXECUTABLES
|
|
62
|
-
})
|
|
62
|
+
}),
|
|
63
|
+
hostCapabilities: [agentSigningCapability]
|
|
63
64
|
});
|
|
64
65
|
var defaultPiDaemonAdapter = createPiDaemonAdapter(defaultPiRuntimeDefinition);
|
|
65
66
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.1",
|
|
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/agent-runtime": "0.
|
|
81
|
+
"@themoltnet/agent-runtime": "0.44.0",
|
|
82
82
|
"@themoltnet/os-keyring": "0.2.0",
|
|
83
|
-
"@themoltnet/pi-runtime": "0.
|
|
84
|
-
"@themoltnet/sdk": "0.
|
|
83
|
+
"@themoltnet/pi-runtime": "0.12.1",
|
|
84
|
+
"@themoltnet/sdk": "0.137.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"tsx": "^4.7.0",
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"vite-plugin-dts": "^4.5.4",
|
|
91
91
|
"vitest": "^3.0.0",
|
|
92
92
|
"@moltnet/bootstrap": "0.1.0",
|
|
93
|
+
"@moltnet/crypto-service": "0.1.0",
|
|
93
94
|
"@moltnet/models": "0.1.0",
|
|
95
|
+
"@moltnet/observability": "0.1.0",
|
|
94
96
|
"@moltnet/runtime-profiles": "0.1.0",
|
|
95
|
-
"@moltnet/tasks": "0.1.0"
|
|
96
|
-
"@moltnet/crypto-service": "0.1.0",
|
|
97
|
-
"@moltnet/observability": "0.1.0"
|
|
97
|
+
"@moltnet/tasks": "0.1.0"
|
|
98
98
|
},
|
|
99
99
|
"nx": {
|
|
100
100
|
"projectType": "application",
|