@themoltnet/agent-daemon 0.44.1 → 0.46.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/dist/pi.d.ts.map CHANGED
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AACA,OAAO,EASL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,cAAc,CAAC;AAEhF,eAAO,MAAM,oBAAoB,kkBAIvB,CAAC;AAEX,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,GAC3B,oBAAoB,CAkEtB;AAED,eAAO,MAAM,0BAA0B,qBASrC,CAAC;AAEH,eAAO,MAAM,sBAAsB,sBAElC,CAAC"}
package/dist/pi.js CHANGED
@@ -1,5 +1,558 @@
1
1
  #!/usr/bin/env node
2
+ import { Type } from "typebox";
3
+ import { CID } from "multiformats/cid";
4
+ import * as json from "multiformats/codecs/json";
5
+ import { sha256 } from "multiformats/hashes/sha2";
6
+ import { Value } from "typebox/value";
2
7
  import { GONDOLIN_BASE_EXECUTABLES, GONDOLIN_TOOL_NAMES, MOLTNET_TOOL_NAMES, agentSigningCapability, buildPiExecutorManifest, createPiTaskExecutor, defineGondolinTemplate, definePiRuntime } from "@themoltnet/pi-runtime";
8
+ import { createHash } from "node:crypto";
9
+ import * as ed from "@noble/ed25519";
10
+ import { createHash as createHash$1, randomBytes } from "crypto";
11
+ import "multiformats/codecs/raw";
12
+ import "multiformats/hashes/digest";
13
+ import "@noble/hashes/sha2";
14
+ import "multiformats/bases/base32";
15
+ import { ed25519 } from "@noble/curves/ed25519.js";
16
+ import "@ipld/dag-cbor";
17
+ import "@noble/ciphers/chacha";
18
+ import "@noble/hashes/hkdf";
19
+ //#region ../../libs/crypto-service/src/ssh.ts
20
+ /**
21
+ * SSH key format conversion for MoltNet Ed25519 keys
22
+ *
23
+ * Converts MoltNet agent keys (ed25519:<base64>) to OpenSSH format
24
+ * for use with git commit signing and SSH authentication.
25
+ */
26
+ if (!ed.etc.sha512Sync) ed.etc.sha512Sync = (...m) => {
27
+ const hash = createHash$1("sha512");
28
+ m.forEach((msg) => hash.update(msg));
29
+ return hash.digest();
30
+ };
31
+ new TextEncoder();
32
+ //#endregion
33
+ //#region ../../libs/crypto-service/src/crypto.service.ts
34
+ /**
35
+ * MoltNet Crypto Service
36
+ *
37
+ * Ed25519 cryptographic operations for agent identity
38
+ * Uses @noble/ed25519 for pure TypeScript implementation
39
+ */
40
+ ed.etc.sha512Sync = (...m) => {
41
+ const hash = createHash$1("sha512");
42
+ m.forEach((msg) => hash.update(msg));
43
+ return hash.digest();
44
+ };
45
+ /** Domain-separation prefix for the signing payload envelope. */
46
+ var DOMAIN_PREFIX = "moltnet:v1";
47
+ /**
48
+ * Build deterministic signing bytes with domain separation and
49
+ * length-prefixed binary framing.
50
+ *
51
+ * Layout:
52
+ * UTF-8("moltnet:v1") || u32be(len(msg_hash)) || msg_hash || u32be(len(nonce_bytes)) || nonce_bytes
53
+ *
54
+ * Where msg_hash = SHA-256(UTF-8(message)).
55
+ *
56
+ * This produces a fixed-structure byte sequence immune to whitespace,
57
+ * newline, and encoding differences between runtimes.
58
+ */
59
+ function buildSigningBytes(message, nonce) {
60
+ const msgHash = createHash$1("sha256").update(Buffer.from(message, "utf-8")).digest();
61
+ const nonceBytes = Buffer.from(nonce, "utf-8");
62
+ const prefix = Buffer.from(DOMAIN_PREFIX, "utf-8");
63
+ const buf = Buffer.alloc(prefix.length + 4 + msgHash.length + 4 + nonceBytes.length);
64
+ let offset = 0;
65
+ prefix.copy(buf, offset);
66
+ offset += prefix.length;
67
+ buf.writeUInt32BE(msgHash.length, offset);
68
+ offset += 4;
69
+ msgHash.copy(buf, offset);
70
+ offset += msgHash.length;
71
+ buf.writeUInt32BE(nonceBytes.length, offset);
72
+ offset += 4;
73
+ nonceBytes.copy(buf, offset);
74
+ return new Uint8Array(buf);
75
+ }
76
+ var cryptoService = {
77
+ async generateKeyPair() {
78
+ const privateKeyBytes = ed.utils.randomPrivateKey();
79
+ const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
80
+ const privateKey = Buffer.from(privateKeyBytes).toString("base64");
81
+ return {
82
+ publicKey: `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`,
83
+ privateKey,
84
+ fingerprint: this.generateFingerprint(publicKeyBytes)
85
+ };
86
+ },
87
+ generateFingerprint(publicKeyBytes) {
88
+ return (createHash$1("sha256").update(publicKeyBytes).digest("hex").slice(0, 16).toUpperCase().match(/.{4}/g) ?? []).join("-");
89
+ },
90
+ parsePublicKey(publicKey) {
91
+ const base64 = publicKey.replace(/^ed25519:/, "");
92
+ return new Uint8Array(Buffer.from(base64, "base64"));
93
+ },
94
+ async sign(message, privateKeyBase64) {
95
+ const privateKeyBytes = new Uint8Array(Buffer.from(privateKeyBase64, "base64"));
96
+ const messageBytes = new TextEncoder().encode(message);
97
+ const signature = await ed.signAsync(messageBytes, privateKeyBytes);
98
+ return Buffer.from(signature).toString("base64");
99
+ },
100
+ async verify(message, signature, publicKey) {
101
+ try {
102
+ const publicKeyBytes = this.parsePublicKey(publicKey);
103
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
104
+ const messageBytes = new TextEncoder().encode(message);
105
+ return await ed.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
106
+ } catch {
107
+ return false;
108
+ }
109
+ },
110
+ async signWithNonce(message, nonce, privateKeyBase64) {
111
+ const privateKeyBytes = new Uint8Array(Buffer.from(privateKeyBase64, "base64"));
112
+ const signingBytes = buildSigningBytes(message, nonce);
113
+ const signature = await ed.signAsync(signingBytes, privateKeyBytes);
114
+ return Buffer.from(signature).toString("base64");
115
+ },
116
+ async verifyWithNonce(message, nonce, signature, publicKey) {
117
+ try {
118
+ const publicKeyBytes = this.parsePublicKey(publicKey);
119
+ const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
120
+ const signingBytes = buildSigningBytes(message, nonce);
121
+ return await ed.verifyAsync(signatureBytes, signingBytes, publicKeyBytes);
122
+ } catch {
123
+ return false;
124
+ }
125
+ },
126
+ async createSignedMessage(message, privateKeyBase64, publicKey) {
127
+ return {
128
+ message,
129
+ signature: await this.sign(message, privateKeyBase64),
130
+ publicKey
131
+ };
132
+ },
133
+ async verifySignedMessage(signedMessage) {
134
+ return this.verify(signedMessage.message, signedMessage.signature, signedMessage.publicKey);
135
+ },
136
+ generateChallenge() {
137
+ return `moltnet:challenge:${randomBytes(32).toString("hex")}:${Date.now()}`;
138
+ },
139
+ async derivePublicKey(privateKeyBase64) {
140
+ const privateKeyBytes = new Uint8Array(Buffer.from(privateKeyBase64, "base64"));
141
+ const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
142
+ return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
143
+ },
144
+ getFingerprintFromPublicKey(publicKey) {
145
+ const publicKeyBytes = this.parsePublicKey(publicKey);
146
+ return this.generateFingerprint(publicKeyBytes);
147
+ },
148
+ deriveX25519PrivateKey(ed25519PrivateKeyBase64) {
149
+ const seed = new Uint8Array(Buffer.from(ed25519PrivateKeyBase64, "base64"));
150
+ const x25519Priv = ed25519.utils.toMontgomerySecret(seed);
151
+ return Buffer.from(x25519Priv).toString("base64");
152
+ },
153
+ deriveX25519PublicKey(ed25519PublicKey) {
154
+ const edPubBytes = this.parsePublicKey(ed25519PublicKey);
155
+ const x25519Pub = ed25519.utils.toMontgomery(edPubBytes);
156
+ return `x25519:${Buffer.from(x25519Pub).toString("base64")}`;
157
+ },
158
+ async createIdentityProof(identityId, privateKeyBase64) {
159
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
160
+ const message = `moltnet:register:${identityId}:${timestamp}`;
161
+ return {
162
+ message,
163
+ signature: await this.sign(message, privateKeyBase64),
164
+ timestamp
165
+ };
166
+ },
167
+ async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
168
+ if (!await this.verify(proof.message, proof.signature, publicKey)) return false;
169
+ const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
170
+ if (!proof.message.startsWith(expectedPrefix)) return false;
171
+ const proofTime = new Date(proof.timestamp).getTime();
172
+ if (Date.now() - proofTime > 300 * 1e3) return false;
173
+ return true;
174
+ }
175
+ };
176
+ //#endregion
177
+ //#region ../../libs/crypto-service/src/executor-attestation.ts
178
+ ed.etc.sha512Sync = (...m) => {
179
+ const hash = createHash("sha512");
180
+ m.forEach((msg) => hash.update(msg));
181
+ return hash.digest();
182
+ };
183
+ //#endregion
184
+ //#region ../../libs/crypto-service/src/json-cid.ts
185
+ /**
186
+ * Generic JSON CID — CIDv1 for arbitrary JSON-serialisable values.
187
+ *
188
+ * Uses the dag-json codec and sha2-256, producing a base32lower CIDv1.
189
+ * Suitable for content-addressing task inputs, schema objects, and other
190
+ * JSON payloads that don't need diary-entry canonical normalisation.
191
+ */
192
+ async function computeJsonCid(value) {
193
+ const bytes = json.encode(value);
194
+ const hash = await sha256.digest(bytes);
195
+ return CID.create(1, json.code, hash).toString();
196
+ }
197
+ new TextEncoder().encode("SSHSIG");
198
+ //#endregion
199
+ //#region ../../libs/execution-plan/src/control-ids.ts
200
+ var CREDENTIAL_AUTHORITY_PREFIX = "credential:";
201
+ var CREDENTIAL_PROJECTION_PREFIX = "credential-projection:";
202
+ var HOST_CAPABILITY_PREFIX = "host-capability:";
203
+ function credentialAuthorityControl(name) {
204
+ return `${CREDENTIAL_AUTHORITY_PREFIX}${name}`;
205
+ }
206
+ function credentialProjectionControl(projection) {
207
+ return `${CREDENTIAL_PROJECTION_PREFIX}${projection}`;
208
+ }
209
+ function hostCapabilityControl(name) {
210
+ return `${HOST_CAPABILITY_PREFIX}${name}`;
211
+ }
212
+ //#endregion
213
+ //#region ../../libs/execution-plan/src/compile-execution-plan.ts
214
+ /**
215
+ * Compile resolved authority and portable intent against one executor offer.
216
+ * The compiler never reads policy identifiers, host bindings, provider
217
+ * coordinates, runtime manifests, or implementation names.
218
+ */
219
+ function compileExecutionPlan(input) {
220
+ const { intent, offer } = input;
221
+ const stamp = {
222
+ mode: intent.mode,
223
+ phase: "preflight",
224
+ basis: "declared"
225
+ };
226
+ const decisions = [];
227
+ const deliverables = [];
228
+ const readinessByName = uniqueReadiness(input.credentialReadiness);
229
+ const offersByControl = groupOffers(offer.controls);
230
+ const authorized = intent.authority.authorizedControls;
231
+ const networkPatterns = [...intent.network.allowedHosts, ...intent.network.allowedInternalHosts];
232
+ let blocked = false;
233
+ for (const requirement of intent.credentialRequirements) {
234
+ const control = credentialAuthorityControl(requirement.name);
235
+ const fail = (reason, state) => {
236
+ const decisionState = state ?? (requirement.required ? "failed" : "degraded");
237
+ decisions.push({
238
+ control,
239
+ state: decisionState,
240
+ ...stamp,
241
+ reason
242
+ });
243
+ blocked ||= requirement.required;
244
+ };
245
+ if (authorized === void 0) {
246
+ fail("credential_authority_unresolved");
247
+ continue;
248
+ }
249
+ if (!authorized.includes(control)) {
250
+ fail("credential_authority_denied");
251
+ continue;
252
+ }
253
+ const readiness = readinessByName.get(requirement.name);
254
+ if (readiness === void 0) {
255
+ fail("credential_readiness_missing");
256
+ continue;
257
+ }
258
+ if (readiness.status !== "ready") {
259
+ fail(readiness.status);
260
+ continue;
261
+ }
262
+ if (requirement.destinations.some((destination) => !hostCovered(destination.host, networkPatterns))) {
263
+ fail("destination_not_in_network_intent");
264
+ continue;
265
+ }
266
+ if (requirement.lifecycle !== void 0 && requirement.lifecycle.maxTtlSec > intent.lease.ttlSec) {
267
+ fail("lifecycle_exceeds_lease");
268
+ continue;
269
+ }
270
+ const offerControl = credentialProjectionControl(requirement.projection);
271
+ const candidates = offersByControl.get(offerControl) ?? [];
272
+ const matchingCandidates = candidates.filter((candidate) => offerContainsRequirement(candidate, requirement));
273
+ if (matchingCandidates.length !== 1) {
274
+ fail(candidates.length === 0 ? "control_not_offered" : matchingCandidates.length === 0 ? "offer_constraints_mismatch" : "offer_ambiguous", "unsupported");
275
+ continue;
276
+ }
277
+ const candidate = matchingCandidates[0];
278
+ decisions.push({
279
+ control,
280
+ state: "enforced",
281
+ ...stamp,
282
+ offerControl,
283
+ enforcement: candidate.enforcement,
284
+ locus: candidate.locus
285
+ });
286
+ deliverables.push({
287
+ name: requirement.name,
288
+ projection: requirement.projection,
289
+ ...requirement.projection === "brokered-http" && { guestEnv: requirement.guestEnv },
290
+ required: requirement.required,
291
+ destinations: cloneDestinations(requirement.destinations),
292
+ offerControl,
293
+ enforcement: candidate.enforcement,
294
+ locus: candidate.locus
295
+ });
296
+ }
297
+ for (const name of intent.requiredCapabilities) {
298
+ const control = hostCapabilityControl(name);
299
+ if (authorized === void 0) {
300
+ decisions.push({
301
+ control,
302
+ state: "failed",
303
+ ...stamp,
304
+ reason: "capability_authority_unresolved"
305
+ });
306
+ blocked = true;
307
+ continue;
308
+ }
309
+ if (!authorized.includes(control)) {
310
+ decisions.push({
311
+ control,
312
+ state: "failed",
313
+ ...stamp,
314
+ reason: "capability_authority_denied"
315
+ });
316
+ blocked = true;
317
+ continue;
318
+ }
319
+ const candidates = offersByControl.get(control) ?? [];
320
+ if (candidates.length !== 1) {
321
+ decisions.push({
322
+ control,
323
+ state: "unsupported",
324
+ ...stamp,
325
+ reason: candidates.length === 0 ? "control_not_offered" : "offer_ambiguous"
326
+ });
327
+ blocked = true;
328
+ continue;
329
+ }
330
+ const candidate = candidates[0];
331
+ decisions.push({
332
+ control,
333
+ state: "enforced",
334
+ ...stamp,
335
+ offerControl: candidate.id,
336
+ enforcement: candidate.enforcement,
337
+ locus: candidate.locus
338
+ });
339
+ }
340
+ for (const control of intent.lease.requiredControls) {
341
+ const candidates = offersByControl.get(control) ?? [];
342
+ if (candidates.length !== 1) {
343
+ decisions.push({
344
+ control,
345
+ state: "unsupported",
346
+ ...stamp,
347
+ reason: candidates.length === 0 ? "control_not_offered" : "offer_ambiguous"
348
+ });
349
+ blocked = true;
350
+ continue;
351
+ }
352
+ const candidate = candidates[0];
353
+ decisions.push({
354
+ control,
355
+ state: "enforced",
356
+ ...stamp,
357
+ offerControl: candidate.id,
358
+ enforcement: candidate.enforcement,
359
+ locus: candidate.locus
360
+ });
361
+ }
362
+ return {
363
+ mode: intent.mode,
364
+ launchable: !blocked,
365
+ executor: { ...offer.executor },
366
+ decisions,
367
+ deliverables,
368
+ effectiveNetwork: {
369
+ allowedHosts: [...new Set(intent.network.allowedHosts)].sort(),
370
+ allowedInternalHosts: [...new Set(intent.network.allowedInternalHosts)].sort()
371
+ }
372
+ };
373
+ }
374
+ function groupOffers(controls) {
375
+ const grouped = /* @__PURE__ */ new Map();
376
+ for (const control of controls) {
377
+ const existing = grouped.get(control.id) ?? [];
378
+ existing.push(control);
379
+ grouped.set(control.id, existing);
380
+ }
381
+ return grouped;
382
+ }
383
+ function uniqueReadiness(readiness) {
384
+ const result = /* @__PURE__ */ new Map();
385
+ for (const record of readiness) {
386
+ if (result.has(record.name)) throw new Error(`duplicate credential readiness for "${record.name}"`);
387
+ result.set(record.name, record);
388
+ }
389
+ return result;
390
+ }
391
+ function offerContainsRequirement(offer, requirement) {
392
+ const offeredDestinations = offer.constraints?.destinations;
393
+ if (offeredDestinations === void 0 || !sameDestinationSet(offeredDestinations, requirement.destinations)) return false;
394
+ if (requirement.projection === "brokered-http") return offer.constraints?.guestEnvs?.includes(requirement.guestEnv) ?? false;
395
+ return true;
396
+ }
397
+ function sameDestinationSet(offered, requested) {
398
+ const offeredKeys = offered.map(destinationKey).sort();
399
+ const requestedKeys = requested.map(destinationKey).sort();
400
+ return offeredKeys.length === requestedKeys.length && offeredKeys.every((key, index) => key === requestedKeys[index]);
401
+ }
402
+ function destinationKey(destination) {
403
+ return `${destination.protocol}\0${destination.host}\0${destination.port}`;
404
+ }
405
+ function cloneDestinations(destinations) {
406
+ return destinations.map((destination) => ({ ...destination }));
407
+ }
408
+ /** Exact host, or a profile wildcard covering one or more subdomains. */
409
+ function hostCovered(host, patterns) {
410
+ return patterns.some((pattern) => {
411
+ if (pattern === host) return true;
412
+ if (!pattern.startsWith("*.")) return false;
413
+ const suffix = pattern.slice(1);
414
+ return host.endsWith(suffix) && host.length > suffix.length;
415
+ });
416
+ }
417
+ //#endregion
418
+ //#region ../../libs/execution-plan/src/credential-requirements.ts
419
+ var CredentialDestination = Type.Object({
420
+ protocol: Type.Union([Type.Literal("https"), Type.Literal("http")]),
421
+ host: Type.String({
422
+ minLength: 1,
423
+ maxLength: 255,
424
+ pattern: "^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$"
425
+ }),
426
+ port: Type.Integer({
427
+ minimum: 1,
428
+ maximum: 65535
429
+ })
430
+ }, { additionalProperties: false });
431
+ var GuestEnvName = Type.String({
432
+ minLength: 1,
433
+ maxLength: 128,
434
+ pattern: "^(?!MOLTNET_)[A-Z][A-Z0-9_]*$"
435
+ });
436
+ var CredentialLifecycleIntent = Type.Object({
437
+ maxTtlSec: Type.Integer({
438
+ minimum: 1,
439
+ maximum: 86400
440
+ }),
441
+ refreshBeforeSec: Type.Integer({
442
+ minimum: 1,
443
+ maximum: 86400
444
+ })
445
+ }, { additionalProperties: false });
446
+ var credentialRequirementBase = {
447
+ name: Type.String({ minLength: 1 }),
448
+ kind: Type.Union([
449
+ Type.Literal("http-bearer"),
450
+ Type.Literal("http-basic"),
451
+ Type.Literal("api-key-header")
452
+ ]),
453
+ destinations: Type.Array(CredentialDestination, { minItems: 1 }),
454
+ required: Type.Boolean({ default: true }),
455
+ lifecycle: Type.Optional(CredentialLifecycleIntent)
456
+ };
457
+ var HostToolCredentialRequirement = Type.Object({
458
+ ...credentialRequirementBase,
459
+ projection: Type.Literal("host-tool")
460
+ }, { additionalProperties: false });
461
+ var BrokeredHttpCredentialRequirement = Type.Object({
462
+ ...credentialRequirementBase,
463
+ projection: Type.Literal("brokered-http"),
464
+ guestEnv: GuestEnvName
465
+ }, { additionalProperties: false });
466
+ var CredentialRequirement = Type.Union([HostToolCredentialRequirement, BrokeredHttpCredentialRequirement]);
467
+ function parseCredentialRequirements(input) {
468
+ const withDefaults = Value.Default(Type.Array(CredentialRequirement), Value.Clone(input));
469
+ const requirements = Value.Parse(Type.Array(CredentialRequirement), withDefaults);
470
+ const names = /* @__PURE__ */ new Set();
471
+ const guestEnvs = /* @__PURE__ */ new Set();
472
+ for (const requirement of requirements) {
473
+ if (names.has(requirement.name)) throw new Error(`duplicate credential requirement name "${requirement.name}"`);
474
+ names.add(requirement.name);
475
+ if (requirement.projection === "brokered-http") {
476
+ if (guestEnvs.has(requirement.guestEnv)) throw new Error(`duplicate brokered guestEnv "${requirement.guestEnv}"`);
477
+ guestEnvs.add(requirement.guestEnv);
478
+ }
479
+ const lifecycle = requirement.lifecycle;
480
+ if (lifecycle && lifecycle.refreshBeforeSec >= lifecycle.maxTtlSec) throw new Error(`credential requirement "${requirement.name}": refreshBeforeSec must be shorter than maxTtlSec`);
481
+ }
482
+ return requirements;
483
+ }
484
+ //#endregion
485
+ //#region ../../libs/execution-plan/src/execution-snapshot.ts
486
+ /**
487
+ * Create an immutable, content-addressed, value-free execution snapshot. The
488
+ * existing policy snapshot hash is pinned inside intent; policy composition
489
+ * has already happened before this boundary.
490
+ */
491
+ async function createExecutionPlanSnapshot(input) {
492
+ const body = structuredClone(input);
493
+ return deepFreeze({
494
+ cid: await computeJsonCid(snapshotBody(body)),
495
+ ...body
496
+ });
497
+ }
498
+ function snapshotBody(input) {
499
+ return {
500
+ v: "moltnet:execution-plan-snapshot:v1",
501
+ ...input
502
+ };
503
+ }
504
+ function deepFreeze(value) {
505
+ if (value !== null && typeof value === "object") {
506
+ for (const child of Object.values(value)) deepFreeze(child);
507
+ Object.freeze(value);
508
+ }
509
+ return value;
510
+ }
511
+ //#endregion
512
+ //#region src/lib/runtime-governance.ts
513
+ var executionOffers = /* @__PURE__ */ new WeakMap();
514
+ /** Attach private governance metadata without widening the public adapter API. */
515
+ function registerRuntimeExecutionOffer(runtime, factory) {
516
+ executionOffers.set(runtime, factory);
517
+ }
518
+ /** Resolve the selected runtime's offer for the observe-only orchestrator. */
519
+ function runtimeExecutionOffer(runtime, executorFingerprint) {
520
+ return executionOffers.get(runtime)?.(executorFingerprint);
521
+ }
522
+ //#endregion
523
+ //#region ../../libs/execution-integrations/src/pi.ts
524
+ /** Convert the canonical Pi manifest to a portable, open-ended offer. */
525
+ function executionCapabilityOfferFromPiManifest(manifest, options) {
526
+ const enforcement = options.enforcement ?? "native";
527
+ const locus = options.locus ?? "pi:isolated-runtime";
528
+ return {
529
+ executor: {
530
+ id: `${manifest.runtime.id}@${manifest.runtime.version}`,
531
+ fingerprint: options.executorFingerprint
532
+ },
533
+ controls: [...(manifest.brokeredHttpSecrets ?? []).map((secret) => ({
534
+ id: credentialProjectionControl("brokered-http"),
535
+ enforcement,
536
+ locus,
537
+ constraints: {
538
+ destinations: manifestDestinations(secret),
539
+ guestEnvs: [secret.guestEnv]
540
+ }
541
+ })), ...(manifest.hostCapabilities ?? []).map((capability) => ({
542
+ id: hostCapabilityControl(capability.name),
543
+ enforcement,
544
+ locus
545
+ }))]
546
+ };
547
+ }
548
+ function manifestDestinations(secret) {
549
+ return secret.hosts.flatMap((host) => secret.ports.map((port) => ({
550
+ protocol: secret.protocol,
551
+ host,
552
+ port
553
+ })));
554
+ }
555
+ //#endregion
3
556
  //#region src/pi.ts
4
557
  var PI_KERNEL_TOOL_NAMES = [
5
558
  ...GONDOLIN_TOOL_NAMES,
@@ -29,7 +582,7 @@ function createPiDaemonAdapter(runtime) {
29
582
  builtInToolNames: PI_KERNEL_TOOL_NAMES
30
583
  });
31
584
  const extensionTools = runtime.extensions.flatMap((extension) => extension.declaredTools);
32
- return {
585
+ const prepared = {
33
586
  runtimeKind: runtime.runtimeKind,
34
587
  manifest,
35
588
  tools: [
@@ -49,6 +602,8 @@ function createPiDaemonAdapter(runtime) {
49
602
  resolvedVmTemplate: resolvedTemplate
50
603
  })
51
604
  };
605
+ registerRuntimeExecutionOffer(prepared, (executorFingerprint) => executionCapabilityOfferFromPiManifest(manifest, { executorFingerprint }));
606
+ return prepared;
52
607
  }
53
608
  };
54
609
  }
@@ -64,4 +619,4 @@ var defaultPiRuntimeDefinition = definePiRuntime({
64
619
  });
65
620
  var defaultPiDaemonAdapter = createPiDaemonAdapter(defaultPiRuntimeDefinition);
66
621
  //#endregion
67
- export { PI_KERNEL_TOOL_NAMES, createPiDaemonAdapter, defaultPiDaemonAdapter, defaultPiRuntimeDefinition };
622
+ export { PI_KERNEL_TOOL_NAMES, cryptoService as a, createPiDaemonAdapter, defaultPiDaemonAdapter, defaultPiRuntimeDefinition, compileExecutionPlan as i, createExecutionPlanSnapshot as n, parseCredentialRequirements as r, runtimeExecutionOffer as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.44.1",
3
+ "version": "0.46.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.",
@@ -56,7 +56,7 @@
56
56
  "@noble/curves": "^2.0.0",
57
57
  "@noble/ed25519": "^2.0.0",
58
58
  "@noble/hashes": "^1.7.0",
59
- "@opentelemetry/api": "^1.9.0",
59
+ "@opentelemetry/api": "^1.9.1",
60
60
  "@opentelemetry/exporter-metrics-otlp-proto": "^0.212.0",
61
61
  "@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
62
62
  "@opentelemetry/instrumentation": "^0.212.0",
@@ -78,10 +78,10 @@
78
78
  "pino-opentelemetry-transport": "^3.0.0",
79
79
  "pino-pretty": "^13.1.3",
80
80
  "typebox": "^1.2.8",
81
- "@themoltnet/agent-runtime": "0.44.0",
82
- "@themoltnet/os-keyring": "0.2.0",
83
- "@themoltnet/pi-runtime": "0.12.1",
84
- "@themoltnet/sdk": "0.137.0"
81
+ "@themoltnet/agent-runtime": "0.45.1",
82
+ "@themoltnet/os-keyring": "0.3.0",
83
+ "@themoltnet/pi-runtime": "0.13.0",
84
+ "@themoltnet/sdk": "0.139.0"
85
85
  },
86
86
  "devDependencies": {
87
87
  "tsx": "^4.7.0",
@@ -91,10 +91,12 @@
91
91
  "vitest": "^3.0.0",
92
92
  "@moltnet/bootstrap": "0.1.0",
93
93
  "@moltnet/crypto-service": "0.1.0",
94
+ "@moltnet/execution-integrations": "0.1.0",
94
95
  "@moltnet/models": "0.1.0",
96
+ "@moltnet/execution-plan": "0.1.0",
95
97
  "@moltnet/observability": "0.1.0",
96
- "@moltnet/runtime-profiles": "0.1.0",
97
- "@moltnet/tasks": "0.1.0"
98
+ "@moltnet/tasks": "0.1.0",
99
+ "@moltnet/runtime-profiles": "0.1.0"
98
100
  },
99
101
  "nx": {
100
102
  "projectType": "application",