@themoltnet/agent-daemon 0.58.0 → 0.60.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 +23 -11
  2. package/dist/cli.js +124 -85
  3. package/package.json +12 -12
package/README.md CHANGED
@@ -114,7 +114,7 @@ All config flows from environment variables. The daemon reads them in
114
114
  | Var | Required | Purpose |
115
115
  | --------------------- | ---------------------------------- | ------------------------------------------------------------------------- |
116
116
  | `GIT_CONFIG_GLOBAL` | config-based | Optional git identity path; not needed for configless startup. |
117
- | `MOLTNET_AGENT_NAME` | yes | Agent name (matches `.moltnet/<name>/`). |
117
+ | `MOLTNET_AGENT_NAME` | yes | Central identity alias (`identities/<name>/`). |
118
118
  | `MOLTNET_API_URL` | configless only | Explicit API endpoint; configless runs never read it from `moltnet.json`. |
119
119
  | `MOLTNET_AGENT_KEY` | no | Team- or identity-scoped agent key. Overrides `moltnet.json`. |
120
120
  | `MOLTNET_PRIVATE_KEY` | configless `once`, `poll`, `drain` | Base64 Ed25519 seed used by daemon-owned executor attestation. |
@@ -133,18 +133,19 @@ its gitconfig in `.moltnet/<agent>/`. Three paths create it:
133
133
  does the same for coding agents that need git and GitHub.
134
134
 
135
135
  **The daemon runs on an agent key only.** OAuth2 client_credentials is not
136
- accepted: it hands the daemon the full 17-scope agent grant against a six-scope
136
+ accepted: it hands the daemon the full agent OAuth2 grant against a least-privilege
137
137
  need, and a Hydra token cannot be a Talos derivation parent. A `moltnet.json`
138
- without `agent_key_ref` is refused at startup with the command that fixes it.
138
+ without `agent_key_ref` or `agent_key_refs` is refused at startup with the command that fixes it.
139
139
 
140
140
  The key reaches the daemon two ways, and `MOLTNET_AGENT_KEY` wins when both are
141
141
  present:
142
142
 
143
143
  - **Configless** — `MOLTNET_AGENT_KEY` (or `MOLTNET_AGENT_KEY_REF`) in the
144
144
  environment. No agent files are read at all.
145
- - **From `moltnet.json`** — an `agent_key_ref` pointing at a secret provider,
146
- which is what `moltnet agents keys create --store` writes. The plaintext
147
- secret never lands in the file.
145
+ - **From `moltnet.json`** — `agent_key_refs[teamId]` selects the run team's
146
+ provider reference. `agent_key_ref` is used only if that team has no entry.
147
+ A configured entry that fails never falls back. The plaintext secret never
148
+ lands in the file.
148
149
 
149
150
  Mint or rotate the key with the CLI, which is separate operator tooling and
150
151
  keeps using OAuth2 for its own authentication:
@@ -155,14 +156,21 @@ moltnet agents keys create --agent-id <uuid> --team-id <uuid> \
155
156
  moltnet agents keys rotate <key-id> --team-id <uuid> --store
156
157
  ```
157
158
 
159
+ To enroll the same identity into another team, use
160
+ `moltnet teams join --code <code> --issue-agent-key --store --idempotency-key <uuid>`.
161
+ Each Agent Server run selects its `teamId` before activation and profile lookup;
162
+ concurrent runs do not change a shared team selector. New runs reload credentials,
163
+ so restart active runs after rotating a key. See the
164
+ [enrollment and recovery guide](../../docs/operate/agent-keys.md#enroll-an-existing-agent-into-another-team).
165
+
158
166
  The daemon reconciles a team-bound key against `--team` at startup; an
159
167
  identity-scoped key may select any team where the agent is authorized. It fails
160
168
  fast if the key is rejected, is not an agent, or a team binding mismatches. See
161
169
  [Run the daemon with an agent key](../../docs/operate/agent-keys.md#run-the-daemon-with-an-agent-key).
162
170
 
163
171
  Daemon authentication and the guest boundary are two separate concerns. Where
164
- the agent key comes from (the environment, or an `agent_key_ref` in
165
- `.moltnet/<agent>/moltnet.json` resolved through the host secret provider)
172
+ the agent key comes from (the environment, or a selected key reference in the
173
+ central identity's `moltnet.json` resolved through the host secret provider)
166
174
  decides how the host-side SDK `Agent` is built. The guest boundary is fixed: **the guest never
167
175
  receives MoltNet credential material.** No `.moltnet` file, gitconfig, SSH
168
176
  signing key, GitHub App PEM, or MoltNet environment credential is injected into
@@ -196,9 +204,13 @@ An agent key used by the daemon needs this least-privilege scope set:
196
204
  agent:profile crypto:sign runtime:read task:read task:claim task:execute
197
205
  ```
198
206
 
199
- The Console selects these scopes by default. Knowledge-enabled workers must add
200
- `diary:read`, `diary:write`, `pack:read`, and `pack:write` when the key is
201
- issued.
207
+ The daemon also uses `diary:read`, `team:read` and `team:join` when the key
208
+ carries them -- read access to the agent's teams and their diaries, and
209
+ enrollment into a team. Startup does not check for them: a key without them
210
+ claims and runs work, and the daemon reports which are absent.
211
+
212
+ The Console selects the full set by default. Knowledge-enabled workers must add
213
+ `diary:write`, `pack:read`, and `pack:write` when the key is issued.
202
214
 
203
215
  `crypto:sign` is in the minimum because host-capability signing runs on the
204
216
  daemon's own credential: the local seed signer calls the signing-request
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, cre
14
14
  import { GuestEnvironmentBoundaryError, assertGuestEnvironmentBoundary, createPiRetryTriage, findMainWorktree, isResolvedPathInsideRoot, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveRuntimeProfileModel } from "@themoltnet/pi-runtime";
15
15
  import { FILE_SECRET_PROVIDER, FileSecretProvider, RegisterIdentityError, boundedIdentitySignal, connect, createNodeSecretProviderRegistry, register } from "@themoltnet/sdk/node";
16
16
  import { constants, copyFileSync, createReadStream, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
17
- import { AuthenticationError, IDENTITY_ALIAS_PATTERN, MoltNetError, assertIdentityAlias, assertTrustedConfigApiUrl, createExecutorAttestor, formatSecretReferenceString, getConfigDir, getIdentityDir, isCanonicalConfig, parseSecretReferenceString, readConfig, requireSecureCredentialApiUrl, resolveAgentKey, resolveEnvSecretReference, resolveIdentitySeed } from "@themoltnet/sdk";
17
+ import { AuthenticationError, IDENTITY_ALIAS_PATTERN, MoltNetError, assertIdentityAlias, assertTrustedConfigApiUrl, createExecutorAttestor, formatSecretReferenceString, getConfigDir, getIdentityDir, hasAgentKeyConfiguration, isCanonicalConfig, parseSecretReferenceString, readConfig, requireSecureCredentialApiUrl, resolveAgentKey, resolveEnvSecretReference, resolveIdentitySeed, selectAgentKeyReference } from "@themoltnet/sdk";
18
18
  import { execFile, spawn } from "node:child_process";
19
19
  import { X509Certificate, createHash, createPrivateKey, createPublicKey, randomBytes, randomUUID, timingSafeEqual, webcrypto } from "node:crypto";
20
20
  import { once } from "node:events";
@@ -774,13 +774,24 @@ var CREDENTIAL_SCOPES = {
774
774
  TaskManage: "task:manage",
775
775
  TaskRead: "task:read",
776
776
  TaskWrite: "task:write",
777
+ TeamJoin: "team:join",
777
778
  TeamManage: "team:manage",
778
779
  TeamRead: "team:read"
779
780
  };
780
781
  var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
781
782
  /**
782
- * Minimum grant for the agent daemon. Task credentials attenuate this further
783
- * to `task:execute` alone.
783
+ * What the agent daemon cannot run without, checked against
784
+ * `GET /agents/whoami` at startup. Task credentials attenuate it further to
785
+ * `task:execute` alone.
786
+ *
787
+ * This is the **boot floor**, and deliberately not the same list as
788
+ * `AGENT_CREDENTIAL_SCOPES`. A credential's scopes are fixed when it is minted
789
+ * and `POST /agent-keys` caps a new key at the scopes of the credential
790
+ * requesting it, so no key can ever widen itself. A scope added here therefore
791
+ * stops every daemon already in the field, and only a human with a Console
792
+ * session can mint the replacement. Add one only when the daemon genuinely
793
+ * cannot work without it; anything a caller merely benefits from belongs in
794
+ * `DAEMON_OPTIONAL_SCOPES`, where absence costs a capability instead.
784
795
  *
785
796
  * `crypto:sign` is part of the minimum because host-capability signing runs on
786
797
  * the daemon's own credential: the local seed signer calls the signing-request
@@ -788,7 +799,7 @@ var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
788
799
  * cleanly and then fails the first time guest code signs a diary entry or a
789
800
  * commit.
790
801
  */
791
- var AGENT_CREDENTIAL_SCOPES = [
802
+ var DAEMON_MINIMUM_SCOPES = [
792
803
  CREDENTIAL_SCOPES.AgentProfile,
793
804
  CREDENTIAL_SCOPES.CryptoSign,
794
805
  CREDENTIAL_SCOPES.RuntimeRead,
@@ -796,6 +807,22 @@ var AGENT_CREDENTIAL_SCOPES = [
796
807
  CREDENTIAL_SCOPES.TaskClaim,
797
808
  CREDENTIAL_SCOPES.TaskExecute
798
809
  ];
810
+ /**
811
+ * Read and enrollment authority a daemon uses when it has it, and runs without
812
+ * when it does not: reading the teams it belongs to and their diaries, and
813
+ * joining a team it is not yet a member of.
814
+ *
815
+ * Which product surface each one enables is deliberately not recorded here.
816
+ * That mapping belongs to whatever consumes the scope and changes with it,
817
+ * while the scope names are the contract and do not.
818
+ */
819
+ var DAEMON_OPTIONAL_SCOPES = [
820
+ CREDENTIAL_SCOPES.DiaryRead,
821
+ CREDENTIAL_SCOPES.TeamRead,
822
+ CREDENTIAL_SCOPES.TeamJoin
823
+ ];
824
+ /** What a newly issued agent key should carry: the floor plus the rest. */
825
+ var AGENT_CREDENTIAL_SCOPES = [...DAEMON_MINIMUM_SCOPES, ...DAEMON_OPTIONAL_SCOPES];
799
826
  CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskWrite;
800
827
  CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.DiaryRead, CREDENTIAL_SCOPES.PackRead, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TeamRead;
801
828
  Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile));
@@ -819,6 +846,7 @@ var MCP_CLIENT_SCOPES = [
819
846
  CREDENTIAL_SCOPES.TaskManage,
820
847
  CREDENTIAL_SCOPES.TaskRead,
821
848
  CREDENTIAL_SCOPES.TaskWrite,
849
+ CREDENTIAL_SCOPES.TeamJoin,
822
850
  CREDENTIAL_SCOPES.TeamManage,
823
851
  CREDENTIAL_SCOPES.TeamRead
824
852
  ];
@@ -1172,17 +1200,16 @@ Type.Object({
1172
1200
  Type.Literal("executor"),
1173
1201
  Type.Literal("member")
1174
1202
  ])),
1175
- maxUses: Type.Optional(Type.Integer({
1176
- minimum: 1,
1177
- default: 1
1178
- })),
1179
1203
  expiresInHours: Type.Optional(Type.Integer({
1180
1204
  minimum: 1,
1181
1205
  maximum: 720,
1182
1206
  default: 168
1183
1207
  }))
1184
1208
  });
1185
- Type.Object({ code: Type.String({ minLength: 1 }) });
1209
+ Type.Object({
1210
+ code: Type.String({ minLength: 1 }),
1211
+ issueAgentKey: Type.Optional(Type.Literal(true))
1212
+ });
1186
1213
  Type.Object({ role: Type.Union([
1187
1214
  Type.Literal("manager"),
1188
1215
  Type.Literal("executor"),
@@ -1207,8 +1234,7 @@ Type.Object({
1207
1234
  Type.Literal("executor"),
1208
1235
  Type.Literal("member")
1209
1236
  ]),
1210
- maxUses: Type.Integer(),
1211
- useCount: Type.Integer(),
1237
+ usedAt: Type.Union([Type.String({ format: "date-time" }), Type.Null()]),
1212
1238
  expiresAt: DateTimeUnsafe,
1213
1239
  createdAt: DateTimeUnsafe
1214
1240
  });
@@ -1239,11 +1265,7 @@ Type.Object({
1239
1265
  });
1240
1266
  Type.Object({
1241
1267
  teamId: UuidSchema,
1242
- role: Type.Union([
1243
- Type.Literal("manager"),
1244
- Type.Literal("executor"),
1245
- Type.Literal("member")
1246
- ])
1268
+ role: TeamRoleSchema
1247
1269
  });
1248
1270
  Type.Object({
1249
1271
  updated: Type.Boolean(),
@@ -3165,7 +3187,8 @@ var TaskMessageKind = Type.Union([
3165
3187
  Type.Literal("tool_call_end"),
3166
3188
  Type.Literal("turn_end"),
3167
3189
  Type.Literal("error"),
3168
- Type.Literal("info")
3190
+ Type.Literal("info"),
3191
+ Type.Literal("tool_policy_decision")
3169
3192
  ], { $id: "TaskMessageKind" });
3170
3193
  var Uuid = Type.String({ format: "uuid" });
3171
3194
  var Cid = Type.String({ minLength: 1 });
@@ -3500,10 +3523,11 @@ Run \`agent-daemon <command> --help\` for command-specific flags.
3500
3523
  Prerequisites:
3501
3524
  - configless: MOLTNET_AGENT_KEY (or MOLTNET_AGENT_KEY_REF) and
3502
3525
  MOLTNET_PRIVATE_KEY (or MOLTNET_PRIVATE_KEY_REF); no agent files
3503
- - config-based and sync-sessions: <agent-root>/.moltnet/<agent>/moltnet.json
3504
- carrying agent_key_ref (OAuth2 client credentials are not accepted)
3526
+ - config-based: ~/.config/moltnet/identities/<agent>/moltnet.json
3527
+ carrying agent_key_refs or agent_key_ref (OAuth2 is not accepted)
3528
+ --agent-root explicitly selects a legacy .moltnet/<agent> bundle
3505
3529
 
3506
- No key yet? Mint one with the CLI (--store writes agent_key_ref into
3530
+ No key yet? Mint one with the CLI (--store writes the team slot into
3507
3531
  moltnet.json and keeps the secret in a provider):
3508
3532
 
3509
3533
  moltnet teams list # find the team id
@@ -3711,6 +3735,10 @@ function assessAgentStartupPin(current, expected) {
3711
3735
  };
3712
3736
  return { ok: true };
3713
3737
  }
3738
+ /** A configured team slot must authenticate with that exact team ceiling. */
3739
+ function matchesCredentialTeam(current, credentialTeamId) {
3740
+ return !credentialTeamId || current.credentialBinding?.bindingScope === "team" && current.credentialBinding.boundTeamId === credentialTeamId;
3741
+ }
3714
3742
  //#endregion
3715
3743
  //#region src/lib/agent-context.ts
3716
3744
  /**
@@ -3754,7 +3782,7 @@ function detectCredentialSource(env) {
3754
3782
  function assessStartupBinding(whoami, teamId) {
3755
3783
  if (whoami.subjectType !== "agent") return {
3756
3784
  ok: false,
3757
- reason: `the daemon must authenticate as an agent, but whoami reported subjectType "${whoami.subjectType}". Provide agent credentials (an agent key or the agent's client id/secret).`
3785
+ reason: `the daemon must authenticate as an agent, but whoami reported subjectType "${whoami.subjectType}". Provide agent credentials (an agent key).`
3758
3786
  };
3759
3787
  const boundTeamId = whoami.credentialBinding?.bindingScope === "team" ? whoami.credentialBinding.boundTeamId : void 0;
3760
3788
  if (teamId && boundTeamId && boundTeamId !== teamId) return {
@@ -3767,8 +3795,7 @@ function assessStartupBinding(whoami, teamId) {
3767
3795
  * Validate at startup — after `connect()`, before polling — that the connected
3768
3796
  * credential can operate as `teamId`, failing fast with an actionable message
3769
3797
  * instead of letting an obscure 401/403 surface mid-poll. Runs in both auth
3770
- * modes; in OAuth2 mode it also doubles as an API-reachability and
3771
- * subject-type check. Returns the `whoami` so the caller can log the resolved
3798
+ * credential sources and also checks API reachability and subject type. Returns the `whoami` so the caller can log the resolved
3772
3799
  * identity (never the secret).
3773
3800
  */
3774
3801
  async function validateStartupBinding(options) {
@@ -3784,6 +3811,7 @@ async function validateStartupBinding(options) {
3784
3811
  setTimeout(resolve, 100 * attempt);
3785
3812
  });
3786
3813
  }
3814
+ if (!matchesCredentialTeam(whoami, options.credentialTeamId)) throw new Error("Daemon startup validation failed: selected team credential has a different binding.");
3787
3815
  const assessment = assessStartupBinding(whoami, options.teamId);
3788
3816
  if (!assessment.ok) throw new Error(`Daemon startup validation failed: ${assessment.reason}`);
3789
3817
  const expected = options.expectedAgent;
@@ -3806,9 +3834,9 @@ async function resolveAgentContext(agentName, options = {}) {
3806
3834
  credentialSource: "environment"
3807
3835
  };
3808
3836
  const config = await readConfig(agentDir);
3809
- if (!config?.agent_key_ref) throw new Error(agentKeyRequiredMessage(agentDir, agentName));
3837
+ if (!config || !hasAgentKeyConfiguration(config)) throw new Error(agentKeyRequiredMessage(agentDir, agentName));
3810
3838
  const secretProviders = createNodeSecretProviderRegistry();
3811
- const agentKey = await resolveAgentKey(config, secretProviders);
3839
+ const agentKey = await resolveAgentKey(config, secretProviders, options.teamId);
3812
3840
  if (!agentKey) throw new Error(agentKeyRequiredMessage(agentDir, agentName));
3813
3841
  return {
3814
3842
  agentDir,
@@ -3819,7 +3847,8 @@ async function resolveAgentContext(agentName, options = {}) {
3819
3847
  agentKey,
3820
3848
  apiUrl: resolveConfigApiUrl(config, options.envApiUrl)
3821
3849
  }),
3822
- credentialSource: "config"
3850
+ credentialSource: "config",
3851
+ credentialTeamId: selectAgentKeyReference(config, options.teamId)?.teamId
3823
3852
  };
3824
3853
  }
3825
3854
  /**
@@ -3830,7 +3859,7 @@ async function resolveAgentContext(agentName, options = {}) {
3830
3859
  */
3831
3860
  function agentKeyRequiredMessage(agentDir, agentName) {
3832
3861
  return [
3833
- `${join(agentDir, "moltnet.json")} has no "agent_key_ref". The daemon`,
3862
+ `${join(agentDir, "moltnet.json")} has no "agent_key_ref" or "agent_key_refs". The daemon`,
3834
3863
  `requires a team-bound agent key; OAuth2 client_credentials is no longer`,
3835
3864
  `accepted.`,
3836
3865
  ``,
@@ -3843,7 +3872,7 @@ function agentKeyRequiredMessage(agentDir, agentName) {
3843
3872
  ` --scopes ${DAEMON_KEY_SCOPES.join(",")} \\`,
3844
3873
  ` --store`,
3845
3874
  ``,
3846
- `--store writes "agent_key_ref" into moltnet.json and keeps the secret in`,
3875
+ `--store writes the team slot in "agent_key_refs" and keeps the secret in`,
3847
3876
  `a provider, so the key itself never lands in the file. Omit --scopes to`,
3848
3877
  `get the same daemon minimum by default.`,
3849
3878
  ``,
@@ -4557,7 +4586,14 @@ function recoverScratchWorkspacePath(producer, stateDirs) {
4557
4586
  }
4558
4587
  //#endregion
4559
4588
  //#region src/lib/executor-attestation.ts
4560
- var DAEMON_REQUIRED_SCOPES = AGENT_CREDENTIAL_SCOPES;
4589
+ /**
4590
+ * The startup gate, which is the boot floor rather than the issuance default.
4591
+ * Gating on the wider `AGENT_CREDENTIAL_SCOPES` would refuse every key minted
4592
+ * before a scope was added to it, and a key cannot widen itself.
4593
+ */
4594
+ var DAEMON_REQUIRED_SCOPES = DAEMON_MINIMUM_SCOPES;
4595
+ /** What `moltnet agents keys create` should mint for a new daemon. */
4596
+ var DAEMON_RECOMMENDED_SCOPES = AGENT_CREDENTIAL_SCOPES;
4561
4597
  async function resolveExecutorSigningPrivateKey(input) {
4562
4598
  if (input.credentialSource === "environment") {
4563
4599
  const privateKey = input.configuredPrivateKey.trim();
@@ -4584,7 +4620,7 @@ async function resolveExecutorSigningPrivateKey(input) {
4584
4620
  function validateDaemonScopes(whoami) {
4585
4621
  const available = new Set(whoami.scopes ?? []);
4586
4622
  const missing = DAEMON_REQUIRED_SCOPES.filter((scope) => !available.has(scope));
4587
- if (missing.length > 0) throw new Error(`Daemon startup credential is missing required scopes: ${missing.join(" ")}. Issue a replacement credential with ${DAEMON_REQUIRED_SCOPES.join(" ")}.`);
4623
+ if (missing.length > 0) throw new Error(`Daemon startup credential is missing required scopes: ${missing.join(" ")}. Issue a replacement credential with ${DAEMON_RECOMMENDED_SCOPES.join(" ")}.`);
4588
4624
  }
4589
4625
  async function validateExecutorSigningIdentity(input) {
4590
4626
  let publicKey;
@@ -6532,7 +6568,7 @@ function makeTurnEventHandler(base, context = {}) {
6532
6568
  });
6533
6569
  return (event, summary) => {
6534
6570
  if (event === "text_delta") return;
6535
- log[event === "error" ? "warn" : event === "turn_end" ? "info" : "debug"]({
6571
+ log[event === "error" || event === "tool_policy_decision" && summary.decision === "blocked" ? "warn" : event === "turn_end" || event === "tool_policy_decision" ? "info" : "debug"]({
6536
6572
  event,
6537
6573
  ...summary
6538
6574
  }, `turn.${event}`);
@@ -6631,11 +6667,13 @@ async function runPolling(opts) {
6631
6667
  const resolvedContext = await resolveAgentContext(identity.agent, {
6632
6668
  agentRootDir: explicitAgentRootDir,
6633
6669
  credentialSource: cfg.credentialSource,
6634
- envApiUrl: cfg.apiUrl
6670
+ envApiUrl: cfg.apiUrl,
6671
+ teamId
6635
6672
  });
6636
6673
  gate = "authenticate_and_bind";
6637
6674
  const whoami = await validateStartupBinding({
6638
6675
  agent: resolvedContext.agent,
6676
+ credentialTeamId: resolvedContext.credentialTeamId,
6639
6677
  teamId,
6640
6678
  expectedAgent: cfg.expectedAgent
6641
6679
  });
@@ -7238,11 +7276,13 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
7238
7276
  const resolvedContext = await resolveAgentContext(identity.agent, {
7239
7277
  agentRootDir: explicitAgentRootDir,
7240
7278
  credentialSource: cfg.credentialSource,
7241
- envApiUrl: cfg.apiUrl
7279
+ envApiUrl: cfg.apiUrl,
7280
+ teamId: values.team
7242
7281
  });
7243
7282
  gate = "authenticate_and_bind";
7244
7283
  const whoami = await validateStartupBinding({
7245
7284
  agent: resolvedContext.agent,
7285
+ credentialTeamId: resolvedContext.credentialTeamId,
7246
7286
  teamId: values.team,
7247
7287
  expectedAgent: cfg.expectedAgent
7248
7288
  });
@@ -9539,9 +9579,10 @@ async function createManagedAgent(store, secrets, input, connectAgent = connect)
9539
9579
  */
9540
9580
  function incompleteRegistrationMessage(alias, known) {
9541
9581
  const endpoint = `POST /v1/agents/${alias}/reconcile`;
9582
+ const recovery = known.recoveryPath ? ` Inspect the protected recovery file at ${known.recoveryPath}; it may be incomplete.` : "";
9542
9583
  const reconcile = known.configPath ? `Finish it with ${endpoint} and {"action":"resume"}, or discard the local record with {"action":"abandon"}.` : `No local config was written, so it cannot be resumed; discard the local record with ${endpoint} and {"action":"abandon"}.`;
9543
- if (known.subjectId) return `the remote agent ${known.subjectId} was registered but local activation is incomplete. ${reconcile}`;
9544
- return `registration for "${alias}" may have completed on the server${known.fingerprint ? ` (fingerprint ${known.fingerprint})` : ""}; look the agent up first. ${reconcile}`;
9584
+ if (known.subjectId) return `the remote agent ${known.subjectId} was registered but local activation is incomplete. ${reconcile}${recovery}`;
9585
+ return `registration for "${alias}" may have completed on the server${known.fingerprint ? ` (fingerprint ${known.fingerprint})` : ""}; look the agent up first. ${reconcile}${recovery}`;
9545
9586
  }
9546
9587
  /** Resume a fully persisted registration or explicitly abandon local recovery. */
9547
9588
  async function reconcileManagedRegistration(store, secrets, aliasInput, action, connectAgent = connect, signal) {
@@ -9562,8 +9603,9 @@ async function reconcileManagedRegistration(store, secrets, aliasInput, action,
9562
9603
  store.clearPendingRegistration(alias);
9563
9604
  return null;
9564
9605
  }
9565
- if (!config?.agent_key_ref || config.agent_key_ref.provider !== FILE_SECRET_PROVIDER || config.keys.private_key_ref?.provider !== FILE_SECRET_PROVIDER) throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" does not have complete managed references`);
9566
- const [agentKey, privateKeyState] = await Promise.all([secrets.read(config.agent_key_ref.key), secrets.probe(config.keys.private_key_ref.key)]);
9606
+ const reference = config ? selectAgentKeyReference(config)?.reference : void 0;
9607
+ if (!config || !reference || reference.provider !== FILE_SECRET_PROVIDER || config.keys.private_key_ref?.provider !== FILE_SECRET_PROVIDER) throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" does not have complete managed references`);
9608
+ const [agentKey, privateKeyState] = await Promise.all([secrets.read(reference.key), secrets.probe(config.keys.private_key_ref.key)]);
9567
9609
  if (!agentKey || privateKeyState !== "present") throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" is missing persisted secret material`);
9568
9610
  const apiUrl = requireConfigApiUrl(config, store.agentPath(alias));
9569
9611
  const whoami = await callWhoami(connectAgent, {
@@ -9599,9 +9641,9 @@ async function attachExternalAgent(store, secretProviders, input, connectAgent =
9599
9641
  try {
9600
9642
  if (!central) externalAgentLocation(configPath);
9601
9643
  const config = await readCurrentConfig(configPath);
9602
- if (central && !config.agent_key_ref) throw new AgentServerIdentityError("unsupported_credential", `central identity "${alias}" needs a stored agent key before the Agent Server can run it`);
9644
+ if (central && !hasAgentKeyConfiguration(config)) throw new AgentServerIdentityError("unsupported_credential", `central identity "${alias}" needs a stored agent key before the Agent Server can run it`);
9603
9645
  const configApiUrl = requireTrustedConfigApiUrl(config, configPath);
9604
- const whoami = await authenticateConfig(config, configPath, requireTrustedApiOverride(input.apiUrl, configApiUrl, configPath), secretProviders, connectAgent, input.signal);
9646
+ const whoami = await authenticateConfig(config, configPath, requireTrustedApiOverride(input.apiUrl, configApiUrl, configPath), secretProviders, connectAgent, input.signal, input.teamId);
9605
9647
  const identity = identityFromConfig(config);
9606
9648
  assertIdentityMatches(identity, whoami, `external config ${configPath}`, "authenticated whoami");
9607
9649
  assertSubjectMatches(whoami, config, "authenticated whoami", `external config ${configPath}`);
@@ -9628,15 +9670,16 @@ async function attachExternalAgent(store, secretProviders, input, connectAgent =
9628
9670
  }
9629
9671
  }
9630
9672
  /** Load and authenticate the current config, then refresh its derived pin. */
9631
- async function verifyAgentActivation(store, alias, managedSecretProviders, externalSecretProviders, connectAgent = connect, signal) {
9673
+ async function verifyAgentActivation(store, alias, managedSecretProviders, externalSecretProviders, connectAgent = connect, signal, teamId) {
9632
9674
  const activation = requireActivation(store, alias);
9633
- const verified = activation.source === "managed" ? await verifyManagedActivation(store, activation, managedSecretProviders, connectAgent, signal) : await verifyExternalActivation(store, activation, externalSecretProviders, connectAgent, signal);
9675
+ teamId ??= activation.boundTeamId;
9676
+ const verified = activation.source === "managed" ? await verifyManagedActivation(store, activation, managedSecretProviders, connectAgent, signal, teamId) : await verifyExternalActivation(store, activation, externalSecretProviders, connectAgent, signal, teamId);
9634
9677
  assertSubjectMatches(verified.whoami, verified.config, "authenticated whoami", `agent "${activation.alias}" config`);
9635
9678
  if (verified.whoami.subjectId !== activation.subjectId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami subject does not match agent "${activation.alias}" pinned activation`);
9636
9679
  const identity = identityFromConfig(verified.config);
9637
9680
  assertIdentityMatches(verified.whoami, identity, "authenticated whoami", `agent "${activation.alias}" config`);
9638
9681
  const boundTeamId = boundTeamIdFromWhoami(verified.whoami);
9639
- if (activation.boundTeamId !== boundTeamId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
9682
+ if (!selectAgentKeyReference(verified.config, teamId)?.teamId && activation.boundTeamId !== boundTeamId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
9640
9683
  const refreshed = {
9641
9684
  ...activation,
9642
9685
  ...identity
@@ -9651,36 +9694,26 @@ async function verifyAgentActivation(store, alias, managedSecretProviders, exter
9651
9694
  ...boundTeamId ? { boundTeamId } : {}
9652
9695
  };
9653
9696
  }
9654
- async function verifyManagedActivation(store, activation, secretProviders, connectAgent, signal) {
9697
+ async function verifyManagedActivation(store, activation, secretProviders, connectAgent, signal, teamId) {
9655
9698
  const configPath = store.agentPath(activation.alias);
9656
9699
  const config = await readCurrentConfig(configPath);
9657
9700
  assertActivatedConfig(config, activation, configPath, requireConfigApiUrl(config, configPath), activation.apiUrl);
9658
- let agentKey;
9659
- try {
9660
- agentKey = await resolveAgentKey(config, secretProviders);
9661
- } catch (cause) {
9662
- throw verificationError(`could not resolve the managed agent key for "${activation.alias}"`, cause);
9663
- }
9664
- if (!agentKey) throw new AgentServerIdentityError("verification_failed", `managed config for "${activation.alias}" has no agent_key_ref`);
9665
9701
  return {
9666
9702
  config,
9667
- whoami: await callWhoami(connectAgent, {
9668
- agentKey,
9669
- apiUrl: activation.apiUrl
9670
- }, configPath, signal)
9703
+ whoami: await authenticateConfig(config, configPath, activation.apiUrl, secretProviders, connectAgent, signal, teamId)
9671
9704
  };
9672
9705
  }
9673
- async function verifyExternalActivation(store, activation, secretProviders, connectAgent, signal) {
9706
+ async function verifyExternalActivation(store, activation, secretProviders, connectAgent, signal, teamId) {
9674
9707
  const central = activation.configPath === store.agentPath(activation.alias);
9675
9708
  if (!central) externalAgentLocation(activation.configPath);
9676
9709
  assertTrustedConfigApiUrl(activation.configApiUrl);
9677
9710
  const config = await readCurrentConfig(activation.configPath);
9678
- if (central && !config.agent_key_ref) throw new AgentServerIdentityError("unsupported_credential", `central identity "${activation.alias}" needs a stored agent key before the Agent Server can run it`);
9711
+ if (central && !hasAgentKeyConfiguration(config)) throw new AgentServerIdentityError("unsupported_credential", `central identity "${activation.alias}" needs a stored agent key before the Agent Server can run it`);
9679
9712
  assertActivatedConfig(config, activation, activation.configPath, requireTrustedConfigApiUrl(config, activation.configPath), activation.configApiUrl);
9680
9713
  const effectiveApiUrl = requireTrustedApiOverride(activation.apiUrl, activation.configApiUrl, activation.configPath);
9681
9714
  return {
9682
9715
  config,
9683
- whoami: await authenticateConfig(config, activation.configPath, effectiveApiUrl, secretProviders, connectAgent, signal)
9716
+ whoami: await authenticateConfig(config, activation.configPath, effectiveApiUrl, secretProviders, connectAgent, signal, teamId)
9684
9717
  };
9685
9718
  }
9686
9719
  /** Never let request or activation metadata redirect persisted credentials. */
@@ -9740,18 +9773,20 @@ function externalAgentLocation(configPath) {
9740
9773
  agentRoot: dirname(moltnetDir)
9741
9774
  };
9742
9775
  }
9743
- async function authenticateConfig(config, configPath, apiUrl, secretProviders, connectAgent, signal) {
9776
+ async function authenticateConfig(config, configPath, apiUrl, secretProviders, connectAgent, signal, teamId) {
9744
9777
  let agentKey;
9745
9778
  try {
9746
- agentKey = await resolveAgentKey(config, secretProviders);
9779
+ agentKey = await resolveAgentKey(config, secretProviders, teamId);
9747
9780
  } catch (cause) {
9748
- throw verificationError(`could not resolve the daemon agent key from ${configPath}`, cause);
9781
+ throw verificationError(`could not resolve the daemon agent key from ${configPath}; select a team and enroll or repair its stored key`, cause);
9749
9782
  }
9750
9783
  if (!agentKey) throw new AgentServerIdentityError("unsupported_credential", `external daemon config at ${configPath} must contain an agent_key_ref`);
9751
- return callWhoami(connectAgent, {
9784
+ const whoami = await callWhoami(connectAgent, {
9752
9785
  agentKey,
9753
9786
  ...apiUrl ? { apiUrl } : {}
9754
9787
  }, configPath, signal);
9788
+ if (!matchesCredentialTeam(whoami, selectAgentKeyReference(config, teamId)?.teamId)) throw new AgentServerIdentityError("verification_failed", "authenticated credential team binding does not match the selected team");
9789
+ return whoami;
9755
9790
  }
9756
9791
  async function callWhoami(connectAgent, options, source, signal) {
9757
9792
  try {
@@ -9796,7 +9831,7 @@ function publicAgentView(store, activation) {
9796
9831
  ...activation.boundTeamId ? { teamId: activation.boundTeamId } : {},
9797
9832
  apiUrl: activation.apiUrl,
9798
9833
  createdAt: activation.createdAt,
9799
- hasAgentKey: Boolean(config?.agent_key_ref),
9834
+ hasAgentKey: Boolean(config && hasAgentKeyConfiguration(config)),
9800
9835
  hasPrivateKey: Boolean(config?.keys.private_key_ref)
9801
9836
  };
9802
9837
  }
@@ -9851,7 +9886,6 @@ var INHERITED_MOLTNET_ENV_NAMES = new Set([
9851
9886
  "MOLTNET_CLI_LINUX_BINARY",
9852
9887
  "MOLTNET_CREDENTIAL_BINDINGS",
9853
9888
  "MOLTNET_CREDENTIAL_ENFORCEMENT",
9854
- "MOLTNET_DIARY_ID",
9855
9889
  "MOLTNET_GIT_AUTHOR",
9856
9890
  "MOLTNET_OTEL_ENDPOINT",
9857
9891
  "MOLTNET_PI_VM_INTEGRATION",
@@ -9905,7 +9939,8 @@ var RunManager = class {
9905
9939
  XDG_CACHE_HOME: join(homeDir, ".cache"),
9906
9940
  XDG_CONFIG_HOME: join(homeDir, ".config"),
9907
9941
  XDG_DATA_HOME: join(homeDir, ".local", "share"),
9908
- MOLTNET_TEAM_ID: spec.teamId
9942
+ MOLTNET_TEAM_ID: spec.teamId,
9943
+ ...spec.diaryId ? { MOLTNET_DIARY_ID: spec.diaryId } : {}
9909
9944
  };
9910
9945
  const target = activation.source === "managed" ? {
9911
9946
  agentName: activation.alias,
@@ -9941,15 +9976,16 @@ var RunManager = class {
9941
9976
  ...target.extraArgs
9942
9977
  ];
9943
9978
  if (activation.source === "managed") {
9944
- if (!config.agent_key_ref || !config.keys.private_key_ref) throw new AgentServerRunError("invalid_spec", `managed config for "${activation.alias}" is missing canonical secret references`);
9979
+ const reference = selectAgentKeyReference(config, spec.teamId)?.reference;
9980
+ if (!reference || !config.keys.private_key_ref) throw new AgentServerRunError("invalid_spec", `managed config for "${activation.alias}" is missing canonical secret references`);
9945
9981
  env["MOLTNET_API_URL"] = activation.apiUrl;
9946
- env["MOLTNET_AGENT_KEY_REF"] = formatSecretReferenceString(config.agent_key_ref);
9982
+ env["MOLTNET_AGENT_KEY_REF"] = formatSecretReferenceString(reference);
9947
9983
  env["MOLTNET_PRIVATE_KEY_REF"] = formatSecretReferenceString(config.keys.private_key_ref);
9948
9984
  env["MOLTNET_SECRET_ROOT"] = this.store.secretsDir;
9949
9985
  } else {
9950
9986
  env["MOLTNET_API_URL"] = activation.apiUrl ?? activation.configApiUrl;
9951
9987
  try {
9952
- const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders);
9988
+ const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders, spec.teamId);
9953
9989
  if (!agentKey) throw new Error("external daemon config has no agent key");
9954
9990
  env["MOLTNET_AGENT_KEY"] = agentKey;
9955
9991
  env["MOLTNET_PRIVATE_KEY"] = await resolveIdentitySeed(config, this.options.externalSecretProviders);
@@ -9989,7 +10025,10 @@ var RunManager = class {
9989
10025
  }
9990
10026
  async startReserved(spec, signal) {
9991
10027
  this.assertStartOpen(signal);
9992
- const agent = await (this.options.verifyActivationImpl ?? verifyAgentActivation)(this.store, spec.agent, this.options.secretProviders, this.options.externalSecretProviders, void 0, signal);
10028
+ const agent = await (this.options.verifyActivationImpl ?? verifyAgentActivation)(this.store, spec.agent, this.options.secretProviders, this.options.externalSecretProviders, void 0, signal, spec.teamId).catch((cause) => {
10029
+ if (cause instanceof AgentServerIdentityError && cause.code === "verification_failed") throw new AgentServerIdentityError(cause.code, `Cannot start agent "${spec.agent}" for team "${spec.teamId}": credential verification failed. Check the selected team key and activation.`, { cause });
10030
+ throw cause;
10031
+ });
9993
10032
  this.assertStartOpen(signal);
9994
10033
  if (agent.boundTeamId && agent.boundTeamId !== spec.teamId) throw new AgentServerRunError("invalid_spec", `agent "${spec.agent}" has a key bound to team ${agent.boundTeamId}; start the run with that team, or create a new agent with an enrollment token from team ${spec.teamId}`);
9995
10034
  const id = `${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
@@ -10118,7 +10157,7 @@ var RunManager = class {
10118
10157
  }
10119
10158
  async resolveRuntimeModule(spec, activated, cwd) {
10120
10159
  const profiles = await resolveRuntimeProfiles({
10121
- agent: await this.connectActivatedAgent(activated),
10160
+ agent: await this.connectActivatedAgent(activated, spec.teamId),
10122
10161
  profiles: spec.profiles,
10123
10162
  teamId: spec.teamId,
10124
10163
  cwd
@@ -10140,21 +10179,13 @@ var RunManager = class {
10140
10179
  if (kind === "gondolin_pi") return void 0;
10141
10180
  throw new AgentServerRunError("invalid_spec", `No local runtime is registered for profile kind "${kind}".`);
10142
10181
  }
10143
- async connectActivatedAgent(activated) {
10182
+ async connectActivatedAgent(activated, teamId) {
10144
10183
  const { activation, config } = activated;
10145
- if (activation.source === "managed") {
10146
- const agentKey = await resolveAgentKey(config, this.options.secretProviders);
10147
- if (!agentKey) throw new AgentServerRunError("invalid_spec", `managed agent "${activation.alias}" has no agent key`);
10148
- return connect({
10149
- agentKey,
10150
- apiUrl: activation.apiUrl
10151
- });
10152
- }
10153
- const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders);
10154
- if (!agentKey) throw new AgentServerRunError("invalid_spec", `external agent "${activation.alias}" has no agent key`);
10184
+ const agentKey = await resolveAgentKey(config, activation.source === "managed" ? this.options.secretProviders : this.options.externalSecretProviders, teamId);
10185
+ if (!agentKey) throw new AgentServerRunError("invalid_spec", `agent "${activation.alias}" has no agent key`);
10155
10186
  return connect({
10156
10187
  agentKey,
10157
- apiUrl: activation.apiUrl ?? activation.configApiUrl
10188
+ apiUrl: activation.source === "managed" ? activation.apiUrl : activation.apiUrl ?? activation.configApiUrl
10158
10189
  });
10159
10190
  }
10160
10191
  stop(id) {
@@ -10574,6 +10605,7 @@ var AgentServerRunRecordSchema = Type.Object({
10574
10605
  id: Type.String(),
10575
10606
  agent: Type.String(),
10576
10607
  teamId: Type.String(),
10608
+ diaryId: Type.Optional(Type.String()),
10577
10609
  profiles: StringList,
10578
10610
  taskTypes: StringList,
10579
10611
  mode: Type.Union([Type.Literal("poll"), Type.Literal("drain")]),
@@ -10636,7 +10668,8 @@ var CreateAgentSchema = Type.Union([Type.Object({
10636
10668
  enrollmentToken: Type.String()
10637
10669
  }), Type.Object({
10638
10670
  kind: Type.Literal("external"),
10639
- identityAlias: Type.String()
10671
+ identityAlias: Type.String(),
10672
+ teamId: Type.Optional(Type.String({ format: "uuid" }))
10640
10673
  })]);
10641
10674
  var ReconcileAgentSchema = Type.Object({ action: Type.Union([Type.Literal("resume"), Type.Literal("abandon")]) });
10642
10675
  var ReconcileAgentResultSchema = Type.Union([schemaRef(AgentServerAgentSchema), Type.Object({ abandoned: Type.Literal(true) })], { $id: "ReconcileAgentResult" });
@@ -10651,6 +10684,7 @@ var DiscoverModelsSchema = Type.Object({ models: ProviderModelList }, { $id: "Di
10651
10684
  var StartRunSchema = Type.Object({
10652
10685
  agent: Type.String(),
10653
10686
  teamId: Type.String(),
10687
+ diaryId: Type.Optional(Type.String()),
10654
10688
  profiles: StringList,
10655
10689
  taskTypes: Type.Array(AgentServerTaskTypeSchema),
10656
10690
  mode: Type.Union([Type.Literal("poll"), Type.Literal("drain")])
@@ -11148,6 +11182,7 @@ function registerAgentRoutes(app, options, requirePairedOrigin) {
11148
11182
  const entry = await attachExternalAgent(store, options.externalSecretProviders, {
11149
11183
  name: identityAlias,
11150
11184
  configDir: store.identityDir(identityAlias),
11185
+ ...typeof body["teamId"] === "string" ? { teamId: body["teamId"] } : {},
11151
11186
  signal
11152
11187
  });
11153
11188
  return reply.code(201).send(publicAgentView(store, entry.activation));
@@ -11179,7 +11214,7 @@ function identityViews(store) {
11179
11214
  return store.listIdentityAliases().map((alias) => ({
11180
11215
  alias,
11181
11216
  activated: activated.has(alias),
11182
- hasAgentKey: Boolean(store.readAgentConfig(alias)?.agent_key_ref)
11217
+ hasAgentKey: hasAgentKeyConfiguration(store.readAgentConfig(alias) ?? {})
11183
11218
  }));
11184
11219
  }
11185
11220
  function registerProviderRoutes(app, options, requirePairedOrigin) {
@@ -11263,9 +11298,11 @@ function registerRunRoutes(app, options, requirePairedOrigin) {
11263
11298
  }, async (request, reply) => {
11264
11299
  requirePairedOrigin(request);
11265
11300
  const body = requireBody(request);
11301
+ const diaryId = optionalString(body, "diaryId");
11266
11302
  const record = await runs.start({
11267
11303
  agent: requireString(body, "agent"),
11268
11304
  teamId: requireString(body, "teamId"),
11305
+ ...diaryId ? { diaryId } : {},
11269
11306
  profiles: stringArray(body, "profiles"),
11270
11307
  taskTypes: stringArray(body, "taskTypes"),
11271
11308
  mode: requireString(body, "mode")
@@ -12015,11 +12052,13 @@ async function runSyncSessions(argv) {
12015
12052
  const ctx = await resolveAgentContext(identity.agent, {
12016
12053
  credentialSource: cfg.credentialSource,
12017
12054
  envApiUrl: cfg.apiUrl,
12055
+ teamId: values.team,
12018
12056
  agentRootDir: explicitAgentRootDir
12019
12057
  });
12020
12058
  await validateStartupBinding({
12021
12059
  agent: ctx.agent,
12022
- teamId: values.team
12060
+ teamId: values.team,
12061
+ credentialTeamId: ctx.credentialTeamId
12023
12062
  });
12024
12063
  const stateDirs = ensureDaemonStateDirs(agentRootDir);
12025
12064
  const result = await syncRuntimeSessions({
@@ -12204,7 +12243,7 @@ async function writeCache(cache) {
12204
12243
  }
12205
12244
  //#endregion
12206
12245
  //#region src/version.ts
12207
- var DAEMON_VERSION = "0.58.0";
12246
+ var DAEMON_VERSION = "0.60.0";
12208
12247
  //#endregion
12209
12248
  //#region src/cli.ts
12210
12249
  async function runAgentDaemonCli(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.58.0",
3
+ "version": "0.60.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.",
@@ -85,10 +85,10 @@
85
85
  "proper-lockfile": "4.1.2",
86
86
  "reflect-metadata": "^0.2.2",
87
87
  "typebox": "^1.2.8",
88
- "@themoltnet/agent-runtime": "1.0.3",
89
- "@themoltnet/pi-runtime": "0.17.0",
90
- "@themoltnet/sdk": "0.142.0",
91
- "@themoltnet/os-keyring": "0.3.0"
88
+ "@themoltnet/agent-runtime": "1.1.0",
89
+ "@themoltnet/os-keyring": "0.3.0",
90
+ "@themoltnet/sdk": "0.143.0",
91
+ "@themoltnet/pi-runtime": "0.18.1"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@fastify/swagger": "^9.6.1",
@@ -98,17 +98,17 @@
98
98
  "vite": "^8.0.0",
99
99
  "vite-plugin-dts": "^4.5.4",
100
100
  "vitest": "^3.0.0",
101
- "@moltnet/agent-eval": "0.1.0",
102
101
  "@moltnet/api-client": "0.1.0",
103
- "@moltnet/loopback-companion": "0.1.0",
104
- "@moltnet/execution-plan": "0.1.0",
102
+ "@moltnet/agent-eval": "0.1.0",
105
103
  "@moltnet/bootstrap": "0.1.0",
106
- "@moltnet/models": "0.1.0",
107
- "@moltnet/execution-integrations": "0.1.0",
108
104
  "@moltnet/crypto-service": "0.1.0",
109
- "@moltnet/observability": "0.1.0",
105
+ "@moltnet/execution-integrations": "0.1.0",
106
+ "@moltnet/execution-plan": "0.1.0",
107
+ "@moltnet/loopback-companion": "0.1.0",
110
108
  "@moltnet/runtime-profiles": "0.1.0",
111
- "@moltnet/tasks": "0.1.0"
109
+ "@moltnet/observability": "0.1.0",
110
+ "@moltnet/tasks": "0.1.0",
111
+ "@moltnet/models": "0.1.0"
112
112
  },
113
113
  "nx": {
114
114
  "projectType": "application",