@themoltnet/agent-daemon 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli.js +113 -28
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -94,6 +94,16 @@ fails fast if the key is rejected, is not an agent, or is bound to a different
|
|
|
94
94
|
team. See
|
|
95
95
|
[Run the daemon with an agent key](../../docs/operate/running-agents.md#run-the-daemon-with-an-agent-key).
|
|
96
96
|
|
|
97
|
+
An agent key used by the daemon needs this least-privilege scope set:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
agent:profile runtime:read task:read task:claim task:execute
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The Console selects these five scopes by default. Broader key, diary, pack,
|
|
104
|
+
team-management, and runtime-management scopes are not required for normal
|
|
105
|
+
daemon operation.
|
|
106
|
+
|
|
97
107
|
### Pi provider auth
|
|
98
108
|
|
|
99
109
|
The daemon resolves Pi config from the repository-local `.pi` directory by
|
package/dist/cli.js
CHANGED
|
@@ -253,7 +253,7 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
253
253
|
fragments: {
|
|
254
254
|
"artifact-planner-v1": {
|
|
255
255
|
binding: "prompt_prefix",
|
|
256
|
-
content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts,
|
|
256
|
+
content: "# Bounded artifact planner\n\n- The typed task facts, embedded bounded manifest, exact bound artifact references, registered tools, and runtime capability section are the complete contract. Do not search diaries, inspect a mounted repository, enumerate unrelated tasks or artifacts, modify a checkout, commit, branch, push, or contact GitHub.\n- Read only the exact artifact CIDs named by the task, and only when the embedded manifest does not provide enough evidence. Use the registered task-artifact tools for artifact access; never use shell or CLI wrappers to fetch artifacts, paginate, or discover them speculatively.\n- If the effective runtime exposes a local calculator or shell, use it only inside scratch for coverage accounting, budget arithmetic, and JSON validation. The runtime capability section and policy are authoritative; do not assume a static executable list.\n- Perform semantic classification and planning from supplied content and producer/consumer evidence. Do not substitute filename, directory, language, ecosystem, or repository-specific exclusion rules for evidence.\n- Write and upload exactly the requested versioned plan artifact, then reference its returned metadata through the registered submit-output tool. Do not emit a second prose or JSON representation.",
|
|
257
257
|
slug: "artifact-planner-v1"
|
|
258
258
|
},
|
|
259
259
|
"accountable-delivery-v1": {
|
|
@@ -283,7 +283,7 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
|
|
|
283
283
|
},
|
|
284
284
|
"verification-and-artifacts-v1": {
|
|
285
285
|
binding: "prompt_prefix",
|
|
286
|
-
content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload
|
|
286
|
+
content: "# Verification and artifacts\n\n- Run relevant verification before submitting. When task facts include `successCriteria`, assess them honestly in the generated verification contract; a fail or skip with evidence is better than a fabricated pass.\n- The registered submit-output tool owns the exact agent submission schema and validation recovery. Use that schema; do not invent a JSON shape in prose.\n- Upload only task-relevant artifacts, and inspect each before uploading. Never upload secrets, credentials, API keys, auth tokens or headers, .env files, or personal or customer data; redact sensitive values, and prefer minimal, sanitized excerpts over whole logs, bundles, or datasets. Include artifact metadata only where the typed submit contract permits it.\n- If the task depends on prior artifacts, list and download the exact referenced artifact before judging or continuing that work.",
|
|
287
287
|
slug: "verification-and-artifacts-v1"
|
|
288
288
|
}
|
|
289
289
|
},
|
|
@@ -318,6 +318,46 @@ function deepFreeze(value) {
|
|
|
318
318
|
deepFreeze(RUNTIME_PROFILE_CONTEXT_CATALOGUE);
|
|
319
319
|
Object.freeze(Object.keys(RUNTIME_PROFILE_CONTEXT_CATALOGUE.recipes));
|
|
320
320
|
//#endregion
|
|
321
|
+
//#region ../../libs/models/src/credential-scopes.ts
|
|
322
|
+
var CREDENTIAL_SCOPES = {
|
|
323
|
+
AgentProfile: "agent:profile",
|
|
324
|
+
ConnectorInvoke: "connector:invoke",
|
|
325
|
+
CryptoSign: "crypto:sign",
|
|
326
|
+
DiaryManage: "diary:manage",
|
|
327
|
+
DiaryRead: "diary:read",
|
|
328
|
+
DiaryWrite: "diary:write",
|
|
329
|
+
HumanProfile: "human:profile",
|
|
330
|
+
KeyManage: "key:manage",
|
|
331
|
+
PackRead: "pack:read",
|
|
332
|
+
PackWrite: "pack:write",
|
|
333
|
+
RuntimeManage: "runtime:manage",
|
|
334
|
+
RuntimeRead: "runtime:read",
|
|
335
|
+
TaskClaim: "task:claim",
|
|
336
|
+
TaskExecute: "task:execute",
|
|
337
|
+
TaskManage: "task:manage",
|
|
338
|
+
TaskRead: "task:read",
|
|
339
|
+
TeamManage: "team:manage",
|
|
340
|
+
TeamRead: "team:read"
|
|
341
|
+
};
|
|
342
|
+
var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
|
|
343
|
+
CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskClaim, CREDENTIAL_SCOPES.TaskExecute;
|
|
344
|
+
Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile));
|
|
345
|
+
[
|
|
346
|
+
CREDENTIAL_SCOPES.AgentProfile,
|
|
347
|
+
CREDENTIAL_SCOPES.CryptoSign,
|
|
348
|
+
CREDENTIAL_SCOPES.DiaryManage,
|
|
349
|
+
CREDENTIAL_SCOPES.DiaryRead,
|
|
350
|
+
CREDENTIAL_SCOPES.DiaryWrite,
|
|
351
|
+
CREDENTIAL_SCOPES.HumanProfile,
|
|
352
|
+
CREDENTIAL_SCOPES.PackRead,
|
|
353
|
+
CREDENTIAL_SCOPES.PackWrite,
|
|
354
|
+
CREDENTIAL_SCOPES.TaskExecute,
|
|
355
|
+
CREDENTIAL_SCOPES.TaskManage,
|
|
356
|
+
CREDENTIAL_SCOPES.TaskRead,
|
|
357
|
+
CREDENTIAL_SCOPES.TeamManage,
|
|
358
|
+
CREDENTIAL_SCOPES.TeamRead
|
|
359
|
+
].filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile);
|
|
360
|
+
//#endregion
|
|
321
361
|
//#region ../../libs/models/src/preview-sign.ts
|
|
322
362
|
function schemaRef$1(schema, id) {
|
|
323
363
|
return Type.Unsafe(Type.Ref(id));
|
|
@@ -2538,11 +2578,14 @@ var CuratePackOutput = Type.Object({
|
|
|
2538
2578
|
//#endregion
|
|
2539
2579
|
//#region ../../libs/tasks/src/task-types/freeform.ts
|
|
2540
2580
|
var FREEFORM_TYPE = "freeform";
|
|
2541
|
-
var FreeformExecutionOptions = Type.Object({
|
|
2542
|
-
Type.
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2581
|
+
var FreeformExecutionOptions = Type.Object({
|
|
2582
|
+
workspace: Type.Optional(Type.Union([
|
|
2583
|
+
Type.Literal("none"),
|
|
2584
|
+
Type.Literal("shared_mount"),
|
|
2585
|
+
Type.Literal("dedicated_worktree")
|
|
2586
|
+
])),
|
|
2587
|
+
revision: Type.Optional(Type.String({ pattern: "^[0-9a-fA-F]{40}$" }))
|
|
2588
|
+
}, {
|
|
2546
2589
|
$id: "FreeformExecutionOptions",
|
|
2547
2590
|
additionalProperties: false
|
|
2548
2591
|
});
|
|
@@ -2628,6 +2671,12 @@ var FreeformOutput = Type.Object({
|
|
|
2628
2671
|
* are sequential preconditions, later ones presume earlier ones hold.
|
|
2629
2672
|
*/
|
|
2630
2673
|
async function validateFreeformInputAsync(input, ctx) {
|
|
2674
|
+
const execution = input.execution;
|
|
2675
|
+
if (execution?.revision && execution.workspace === "none") return [{
|
|
2676
|
+
field: "input/execution/revision",
|
|
2677
|
+
message: "execution.revision requires a repository workspace; use shared_mount or dedicated_worktree",
|
|
2678
|
+
code: "freeform.executionRevisionRequiresRepository"
|
|
2679
|
+
}];
|
|
2631
2680
|
const cf = input.continueFrom;
|
|
2632
2681
|
if (!cf) return [];
|
|
2633
2682
|
const source = await ctx.resolveTask(cf.taskId);
|
|
@@ -2641,11 +2690,16 @@ async function validateFreeformInputAsync(input, ctx) {
|
|
|
2641
2690
|
message: `Source task type '${source.taskType}' is not continuable; only freeform → freeform is supported in v1`,
|
|
2642
2691
|
code: "freeform.sourceTaskTypeNotSupported"
|
|
2643
2692
|
}];
|
|
2644
|
-
if (
|
|
2693
|
+
if (execution?.workspace) return [{
|
|
2645
2694
|
field: "input/execution/workspace",
|
|
2646
2695
|
message: "execution.workspace is derived from parent runtime context when continueFrom is set; omit it",
|
|
2647
2696
|
code: "freeform.executionWorkspaceNotInheritable"
|
|
2648
2697
|
}];
|
|
2698
|
+
if (execution?.revision) return [{
|
|
2699
|
+
field: "input/execution/revision",
|
|
2700
|
+
message: "execution.revision is derived from parent runtime context when continueFrom is set; omit it",
|
|
2701
|
+
code: "freeform.executionRevisionNotInheritable"
|
|
2702
|
+
}];
|
|
2649
2703
|
if (ctx.deferReadinessChecks) return [];
|
|
2650
2704
|
const attempt = (await ctx.listAttempts(cf.taskId)).find((a) => a.attemptN === cf.attemptN);
|
|
2651
2705
|
if (!attempt || attempt.status !== "completed") return [{
|
|
@@ -4246,6 +4300,7 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
|
|
|
4246
4300
|
const slotId = slotKey ? buildDaemonSlotId(identity, slotKey) : null;
|
|
4247
4301
|
const sessionDir = slotId ? `${stateDirs.piSessionsDir}/${boundedKeyDirComponent(slotId)}` : null;
|
|
4248
4302
|
const worktreeBranch = resolveTaskWorktreeBranch(task, workspaceMode);
|
|
4303
|
+
const workspaceRevision = resolveTaskWorkspaceRevision(task.input);
|
|
4249
4304
|
const workspaceId = workspaceMode !== "shared_mount" ? resolveTaskWorkspaceId(task, {
|
|
4250
4305
|
sessionKey: slotId,
|
|
4251
4306
|
workspaceScope,
|
|
@@ -4261,9 +4316,14 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
|
|
|
4261
4316
|
workspaceScope,
|
|
4262
4317
|
sessionPersistence: sessionDir ? { sessionDir } : null,
|
|
4263
4318
|
workspaceId,
|
|
4264
|
-
worktreeBranch
|
|
4319
|
+
worktreeBranch,
|
|
4320
|
+
workspaceRevision
|
|
4265
4321
|
};
|
|
4266
4322
|
}
|
|
4323
|
+
function resolveTaskWorkspaceRevision(input) {
|
|
4324
|
+
const value = input?.execution?.revision;
|
|
4325
|
+
return typeof value === "string" && /^[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
|
|
4326
|
+
}
|
|
4267
4327
|
function buildDaemonSlotId(identity, slotKey) {
|
|
4268
4328
|
return [
|
|
4269
4329
|
"agent",
|
|
@@ -4289,6 +4349,7 @@ function slugSlotIdentityComponent(input) {
|
|
|
4289
4349
|
}
|
|
4290
4350
|
function resolveTaskWorktreeBranch(task, workspaceMode) {
|
|
4291
4351
|
if (workspaceMode !== "dedicated_worktree") return null;
|
|
4352
|
+
if (typeof task.input.execution?.revision === "string") return null;
|
|
4292
4353
|
if (task.taskType === "fulfill_brief") {
|
|
4293
4354
|
const input = task.input;
|
|
4294
4355
|
const slug = slugifyAsciiLower(typeof task.title === "string" && task.title.trim().length > 0 ? task.title : typeof input.brief === "string" && input.brief.trim().length > 0 ? input.brief : task.taskType, 60) || "task";
|
|
@@ -4383,9 +4444,14 @@ function createExecutionPlanCache(args) {
|
|
|
4383
4444
|
};
|
|
4384
4445
|
}
|
|
4385
4446
|
function createNullSourceAttemptResolver() {
|
|
4386
|
-
return {
|
|
4387
|
-
|
|
4388
|
-
|
|
4447
|
+
return {
|
|
4448
|
+
findOutputBranch() {
|
|
4449
|
+
return Promise.resolve(null);
|
|
4450
|
+
},
|
|
4451
|
+
findInputRevision() {
|
|
4452
|
+
return Promise.resolve(null);
|
|
4453
|
+
}
|
|
4454
|
+
};
|
|
4389
4455
|
}
|
|
4390
4456
|
function createNullRuntimeSessionStore() {
|
|
4391
4457
|
return {
|
|
@@ -4409,7 +4475,7 @@ function assertPlanAllowedByWorkspacePolicy(plan, policy) {
|
|
|
4409
4475
|
}
|
|
4410
4476
|
function planToRuntimeProfileWorkspaceMode(plan) {
|
|
4411
4477
|
if (plan.workspaceMode === "scratch_mount") return "none";
|
|
4412
|
-
if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch) return "shared_mount";
|
|
4478
|
+
if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch && !plan.workspaceRevision) return "shared_mount";
|
|
4413
4479
|
return plan.workspaceMode;
|
|
4414
4480
|
}
|
|
4415
4481
|
function buildClaimedTaskKey(task) {
|
|
@@ -4455,16 +4521,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4455
4521
|
attemptN: continueFrom.attemptN,
|
|
4456
4522
|
taskId: continueFrom.taskId
|
|
4457
4523
|
});
|
|
4524
|
+
const recoveredRevision = recoveredBranch ? null : await sourceAttemptResolver.findInputRevision({
|
|
4525
|
+
attemptN: continueFrom.attemptN,
|
|
4526
|
+
taskId: continueFrom.taskId
|
|
4527
|
+
});
|
|
4458
4528
|
if (continueFrom.mode === "fork") {
|
|
4459
|
-
if (recoveredBranch) {
|
|
4529
|
+
if (recoveredBranch || recoveredRevision) {
|
|
4460
4530
|
const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
4461
|
-
const forkBranch = buildForkBranch(recoveredBranch, claimedTask.task.id, claimedTask.attemptN);
|
|
4531
|
+
const forkBranch = buildForkBranch(recoveredBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
|
|
4462
4532
|
return {
|
|
4463
4533
|
...basePlan,
|
|
4464
4534
|
workspaceMode: "dedicated_worktree",
|
|
4465
4535
|
workspaceId: forkWorkspaceId,
|
|
4466
4536
|
worktreeBranch: forkBranch,
|
|
4467
|
-
worktreeBaseRef: recoveredBranch,
|
|
4537
|
+
worktreeBaseRef: recoveredBranch ?? recoveredRevision,
|
|
4468
4538
|
workspaceKind: "fork",
|
|
4469
4539
|
sessionPersistence: {
|
|
4470
4540
|
sessionDir,
|
|
@@ -4474,11 +4544,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4474
4544
|
}
|
|
4475
4545
|
throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: durable runtime session is available but the source attempt output did not report a branch`);
|
|
4476
4546
|
}
|
|
4547
|
+
const hasDedicatedWorkspace = Boolean(recoveredBranch || recoveredRevision);
|
|
4477
4548
|
return {
|
|
4478
4549
|
...basePlan,
|
|
4479
|
-
workspaceMode: "dedicated_worktree",
|
|
4480
|
-
workspaceId:
|
|
4550
|
+
workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
|
|
4551
|
+
workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
|
|
4481
4552
|
worktreeBranch: recoveredBranch,
|
|
4553
|
+
workspaceRevision: recoveredRevision,
|
|
4482
4554
|
sessionPersistence: {
|
|
4483
4555
|
sessionDir,
|
|
4484
4556
|
forkFromSessionPath: resolution.sessionPath
|
|
@@ -4486,16 +4558,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4486
4558
|
};
|
|
4487
4559
|
}
|
|
4488
4560
|
const parentBranch = resolution.producerSlot.workspace?.worktreeBranch ?? null;
|
|
4561
|
+
const parentRevision = parentBranch ? null : await sourceAttemptResolver.findInputRevision({
|
|
4562
|
+
attemptN: continueFrom.attemptN,
|
|
4563
|
+
taskId: continueFrom.taskId
|
|
4564
|
+
});
|
|
4489
4565
|
if (continueFrom.mode === "fork") {
|
|
4490
|
-
if (!parentBranch) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch to fork from`);
|
|
4566
|
+
if (!parentBranch && !parentRevision) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch or immutable revision to fork from`);
|
|
4491
4567
|
const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
|
|
4492
|
-
const forkBranch = buildForkBranch(parentBranch, claimedTask.task.id, claimedTask.attemptN);
|
|
4568
|
+
const forkBranch = buildForkBranch(parentBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
|
|
4493
4569
|
return {
|
|
4494
4570
|
...basePlan,
|
|
4495
4571
|
workspaceMode: "dedicated_worktree",
|
|
4496
4572
|
workspaceId: forkWorkspaceId,
|
|
4497
4573
|
worktreeBranch: forkBranch,
|
|
4498
|
-
worktreeBaseRef: parentBranch,
|
|
4574
|
+
worktreeBaseRef: parentBranch ?? parentRevision,
|
|
4499
4575
|
workspaceKind: "fork",
|
|
4500
4576
|
sessionPersistence: {
|
|
4501
4577
|
sessionDir,
|
|
@@ -4503,11 +4579,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
|
|
|
4503
4579
|
}
|
|
4504
4580
|
};
|
|
4505
4581
|
}
|
|
4582
|
+
const hasDedicatedWorkspace = Boolean(parentBranch || parentRevision);
|
|
4506
4583
|
return {
|
|
4507
4584
|
...basePlan,
|
|
4508
|
-
workspaceMode: "dedicated_worktree",
|
|
4509
|
-
workspaceId:
|
|
4585
|
+
workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
|
|
4586
|
+
workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
|
|
4510
4587
|
worktreeBranch: parentBranch,
|
|
4588
|
+
workspaceRevision: parentRevision,
|
|
4511
4589
|
sessionPersistence: {
|
|
4512
4590
|
sessionDir,
|
|
4513
4591
|
forkFromSessionPath: resolution.sessionPath
|
|
@@ -5702,11 +5780,18 @@ function signalExitCode(signal) {
|
|
|
5702
5780
|
//#region src/lib/source-attempts.ts
|
|
5703
5781
|
function createApiSourceAttemptResolver(args) {
|
|
5704
5782
|
const { agent } = args;
|
|
5705
|
-
return {
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5783
|
+
return {
|
|
5784
|
+
async findOutputBranch(input) {
|
|
5785
|
+
const attempt = (await agent.tasks.listAttempts(input.taskId)).find((candidate) => candidate.attemptN === input.attemptN);
|
|
5786
|
+
if (!attempt || attempt.status !== "completed") return null;
|
|
5787
|
+
return resolveOutputBranch(attempt.output);
|
|
5788
|
+
},
|
|
5789
|
+
async findInputRevision(input) {
|
|
5790
|
+
const task = await agent.tasks.get(input.taskId);
|
|
5791
|
+
if (task.status !== "completed" || task.acceptedAttemptN !== input.attemptN) return null;
|
|
5792
|
+
return resolveTaskWorkspaceRevision(task.input);
|
|
5793
|
+
}
|
|
5794
|
+
};
|
|
5710
5795
|
}
|
|
5711
5796
|
function resolveOutputBranch(output) {
|
|
5712
5797
|
if (!output || typeof output !== "object") return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.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.",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"pino": "^10.3.1",
|
|
63
63
|
"pino-pretty": "^13.1.3",
|
|
64
64
|
"typebox": "^1.2.8",
|
|
65
|
-
"@themoltnet/agent-runtime": "0.
|
|
66
|
-
"@themoltnet/
|
|
67
|
-
"@themoltnet/
|
|
65
|
+
"@themoltnet/agent-runtime": "0.40.1",
|
|
66
|
+
"@themoltnet/sdk": "0.129.0",
|
|
67
|
+
"@themoltnet/pi-runtime": "0.6.1"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"tsx": "^4.7.0",
|