@themoltnet/agent-daemon 0.34.2 → 0.36.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 (2) hide show
  1. package/dist/cli.js +106 -41
  2. package/package.json +8 -8
package/dist/cli.js CHANGED
@@ -5,10 +5,10 @@ import * as Format from "typebox/format";
5
5
  import { Type } from "typebox";
6
6
  import crypto from "crypto";
7
7
  import { Value } from "typebox/value";
8
- import { dirname, isAbsolute, join, relative, resolve } from "node:path";
8
+ import { dirname, join, resolve } from "node:path";
9
9
  import { parseArgs, promisify } from "node:util";
10
10
  import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource } from "@themoltnet/agent-runtime";
11
- import { createPiRetryTriage, findMainWorktree, normalizeRetryTriageResult, redactRetryTriageSecrets } from "@themoltnet/pi-runtime";
11
+ import { createPiRetryTriage, findMainWorktree, isResolvedPathInsideRoot, normalizeRetryTriageResult, redactRetryTriageSecrets } from "@themoltnet/pi-runtime";
12
12
  import { execFile, execFileSync } from "node:child_process";
13
13
  import { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from "node:fs";
14
14
  import { AuthenticationError, MoltNetError, connect } from "@themoltnet/sdk";
@@ -251,6 +251,11 @@ Type.Object({
251
251
  var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
252
252
  version: 1,
253
253
  fragments: {
254
+ "artifact-planner-v1": {
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, 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
+ slug: "artifact-planner-v1"
258
+ },
254
259
  "accountable-delivery-v1": {
255
260
  binding: "prompt_prefix",
256
261
  content: "# Accountable delivery\n\n- Pair every commit made during this task with a signed diary entry created by the `moltnet_create_entry` custom tool. Put the returned id in a `MoltNet-Diary: <id>` commit trailer.\n- Keep commit signing enabled; do not bypass the agent git configuration.\n- Push a branch and open or update a pull request only when the task asks for it. For GitHub mutations, use the credential-bound `GH_TOKEN` command form required by the runtime kernel.\n- Keep changes, commits, and any requested pull request coherent enough to review independently.",
@@ -278,11 +283,15 @@ var RUNTIME_PROFILE_CONTEXT_CATALOGUE = {
278
283
  },
279
284
  "verification-and-artifacts-v1": {
280
285
  binding: "prompt_prefix",
281
- 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 large files, binary files, logs, reports, screenshots, traces, bundles, or datasets before submitting. 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.",
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.",
282
287
  slug: "verification-and-artifacts-v1"
283
288
  }
284
289
  },
285
290
  recipes: {
291
+ "artifact-planner@v1": {
292
+ description: "Minimal artifact-only context for bounded semantic classification and planning.",
293
+ fragments: ["artifact-planner-v1"]
294
+ },
286
295
  "run-eval-direct@v1": {
287
296
  description: "Minimal direct context for a short, isolated evaluation run.",
288
297
  fragments: ["run-eval-direct-v1"]
@@ -1721,7 +1730,7 @@ var VerificationResult = Type.Object({
1721
1730
  var VerificationRecord = Type.Object({
1722
1731
  inputCid: Type.String({ minLength: 1 }),
1723
1732
  results: Type.Array(VerificationResult),
1724
- passed: Type.Boolean()
1733
+ passed: Type.Boolean({ description: "True iff every verification result has status \"pass\" or \"skip\"; false when any result has status \"fail\"." })
1725
1734
  }, {
1726
1735
  $id: "VerificationRecord",
1727
1736
  additionalProperties: false
@@ -2529,11 +2538,14 @@ var CuratePackOutput = Type.Object({
2529
2538
  //#endregion
2530
2539
  //#region ../../libs/tasks/src/task-types/freeform.ts
2531
2540
  var FREEFORM_TYPE = "freeform";
2532
- var FreeformExecutionOptions = Type.Object({ workspace: Type.Optional(Type.Union([
2533
- Type.Literal("none"),
2534
- Type.Literal("shared_mount"),
2535
- Type.Literal("dedicated_worktree")
2536
- ])) }, {
2541
+ var FreeformExecutionOptions = Type.Object({
2542
+ workspace: Type.Optional(Type.Union([
2543
+ Type.Literal("none"),
2544
+ Type.Literal("shared_mount"),
2545
+ Type.Literal("dedicated_worktree")
2546
+ ])),
2547
+ revision: Type.Optional(Type.String({ pattern: "^[0-9a-fA-F]{40}$" }))
2548
+ }, {
2537
2549
  $id: "FreeformExecutionOptions",
2538
2550
  additionalProperties: false
2539
2551
  });
@@ -2619,6 +2631,12 @@ var FreeformOutput = Type.Object({
2619
2631
  * are sequential preconditions, later ones presume earlier ones hold.
2620
2632
  */
2621
2633
  async function validateFreeformInputAsync(input, ctx) {
2634
+ const execution = input.execution;
2635
+ if (execution?.revision && execution.workspace === "none") return [{
2636
+ field: "input/execution/revision",
2637
+ message: "execution.revision requires a repository workspace; use shared_mount or dedicated_worktree",
2638
+ code: "freeform.executionRevisionRequiresRepository"
2639
+ }];
2622
2640
  const cf = input.continueFrom;
2623
2641
  if (!cf) return [];
2624
2642
  const source = await ctx.resolveTask(cf.taskId);
@@ -2632,11 +2650,16 @@ async function validateFreeformInputAsync(input, ctx) {
2632
2650
  message: `Source task type '${source.taskType}' is not continuable; only freeform → freeform is supported in v1`,
2633
2651
  code: "freeform.sourceTaskTypeNotSupported"
2634
2652
  }];
2635
- if (input.execution?.workspace) return [{
2653
+ if (execution?.workspace) return [{
2636
2654
  field: "input/execution/workspace",
2637
2655
  message: "execution.workspace is derived from parent runtime context when continueFrom is set; omit it",
2638
2656
  code: "freeform.executionWorkspaceNotInheritable"
2639
2657
  }];
2658
+ if (execution?.revision) return [{
2659
+ field: "input/execution/revision",
2660
+ message: "execution.revision is derived from parent runtime context when continueFrom is set; omit it",
2661
+ code: "freeform.executionRevisionNotInheritable"
2662
+ }];
2640
2663
  if (ctx.deferReadinessChecks) return [];
2641
2664
  const attempt = (await ctx.listAttempts(cf.taskId)).find((a) => a.attemptN === cf.attemptN);
2642
2665
  if (!attempt || attempt.status !== "completed") return [{
@@ -4235,8 +4258,9 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
4235
4258
  const slotKey = warmSessionTtlSec > 0 && descriptor.sessionKey ? buildRuntimeSlotKey(descriptor.sessionKey, identity.runtimeInstanceId) : null;
4236
4259
  const workspaceScope = slotKey !== null ? descriptor.policy.workspaceScope : "attempt";
4237
4260
  const slotId = slotKey ? buildDaemonSlotId(identity, slotKey) : null;
4238
- const sessionDir = slotId ? `${stateDirs.piSessionsDir}/${encodeURIComponent(slotId)}` : null;
4261
+ const sessionDir = slotId ? `${stateDirs.piSessionsDir}/${boundedKeyDirComponent(slotId)}` : null;
4239
4262
  const worktreeBranch = resolveTaskWorktreeBranch(task, workspaceMode);
4263
+ const workspaceRevision = resolveTaskWorkspaceRevision(task.input);
4240
4264
  const workspaceId = workspaceMode !== "shared_mount" ? resolveTaskWorkspaceId(task, {
4241
4265
  sessionKey: slotId,
4242
4266
  workspaceScope,
@@ -4252,9 +4276,14 @@ function buildDaemonTaskExecutionPlan(task, stateDirs, identity, warmSessionTtlS
4252
4276
  workspaceScope,
4253
4277
  sessionPersistence: sessionDir ? { sessionDir } : null,
4254
4278
  workspaceId,
4255
- worktreeBranch
4279
+ worktreeBranch,
4280
+ workspaceRevision
4256
4281
  };
4257
4282
  }
4283
+ function resolveTaskWorkspaceRevision(input) {
4284
+ const value = input?.execution?.revision;
4285
+ return typeof value === "string" && /^[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
4286
+ }
4258
4287
  function buildDaemonSlotId(identity, slotKey) {
4259
4288
  return [
4260
4289
  "agent",
@@ -4280,6 +4309,7 @@ function slugSlotIdentityComponent(input) {
4280
4309
  }
4281
4310
  function resolveTaskWorktreeBranch(task, workspaceMode) {
4282
4311
  if (workspaceMode !== "dedicated_worktree") return null;
4312
+ if (typeof task.input.execution?.revision === "string") return null;
4283
4313
  if (task.taskType === "fulfill_brief") {
4284
4314
  const input = task.input;
4285
4315
  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";
@@ -4323,8 +4353,27 @@ function isRuntimeProfileWorkspaceMode(value) {
4323
4353
  function toDaemonWorkspaceMode(mode) {
4324
4354
  return mode === "none" ? "scratch_mount" : mode;
4325
4355
  }
4356
+ /**
4357
+ * URL-encode a slot/session key for use as a single filesystem directory
4358
+ * component, bounded to stay under the 255-byte per-component filename limit.
4359
+ * A long key (e.g. `run_eval`'s custom key, which embeds the variant label +
4360
+ * agent name + worker id) otherwise crashes `mkdir` with `ENAMETOOLONG`.
4361
+ *
4362
+ * Additive by design: keys short enough today keep their exact encoded form (so
4363
+ * existing warm dirs are byte-identical), and only over-long keys get a readable
4364
+ * prefix + a collision-resistant sha256 suffix. The component is never decoded
4365
+ * back to the key anywhere, so hashing the tail is safe. Used for BOTH the
4366
+ * pi-sessions dir and the session workspace dir so the two names derive
4367
+ * consistently from the same slot id.
4368
+ */
4369
+ function boundedKeyDirComponent(key) {
4370
+ const encoded = encodeURIComponent(key);
4371
+ if (encoded.length <= 200) return encoded;
4372
+ const hash = createHash("sha256").update(key).digest("hex").slice(0, 16);
4373
+ return `${encoded.slice(0, 180)}-${hash}`;
4374
+ }
4326
4375
  function resolveTaskWorkspaceId(task, executionPlan) {
4327
- if (executionPlan.workspaceScope === "session" && executionPlan.sessionKey !== null) return `session-${encodeURIComponent(executionPlan.sessionKey)}`;
4376
+ if (executionPlan.workspaceScope === "session" && executionPlan.sessionKey !== null) return `session-${boundedKeyDirComponent(executionPlan.sessionKey)}`;
4328
4377
  return executionPlan.attemptN ? `daemon-task-${task.id}-attempt-${executionPlan.attemptN}` : `task-${task.id}`;
4329
4378
  }
4330
4379
  //#endregion
@@ -4355,9 +4404,14 @@ function createExecutionPlanCache(args) {
4355
4404
  };
4356
4405
  }
4357
4406
  function createNullSourceAttemptResolver() {
4358
- return { findOutputBranch() {
4359
- return Promise.resolve(null);
4360
- } };
4407
+ return {
4408
+ findOutputBranch() {
4409
+ return Promise.resolve(null);
4410
+ },
4411
+ findInputRevision() {
4412
+ return Promise.resolve(null);
4413
+ }
4414
+ };
4361
4415
  }
4362
4416
  function createNullRuntimeSessionStore() {
4363
4417
  return {
@@ -4381,7 +4435,7 @@ function assertPlanAllowedByWorkspacePolicy(plan, policy) {
4381
4435
  }
4382
4436
  function planToRuntimeProfileWorkspaceMode(plan) {
4383
4437
  if (plan.workspaceMode === "scratch_mount") return "none";
4384
- if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch) return "shared_mount";
4438
+ if (plan.workspaceMode === "dedicated_worktree" && !plan.worktreeBranch && !plan.workspaceRevision) return "shared_mount";
4385
4439
  return plan.workspaceMode;
4386
4440
  }
4387
4441
  function buildClaimedTaskKey(task) {
@@ -4427,16 +4481,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
4427
4481
  attemptN: continueFrom.attemptN,
4428
4482
  taskId: continueFrom.taskId
4429
4483
  });
4484
+ const recoveredRevision = recoveredBranch ? null : await sourceAttemptResolver.findInputRevision({
4485
+ attemptN: continueFrom.attemptN,
4486
+ taskId: continueFrom.taskId
4487
+ });
4430
4488
  if (continueFrom.mode === "fork") {
4431
- if (recoveredBranch) {
4489
+ if (recoveredBranch || recoveredRevision) {
4432
4490
  const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
4433
- const forkBranch = buildForkBranch(recoveredBranch, claimedTask.task.id, claimedTask.attemptN);
4491
+ const forkBranch = buildForkBranch(recoveredBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
4434
4492
  return {
4435
4493
  ...basePlan,
4436
4494
  workspaceMode: "dedicated_worktree",
4437
4495
  workspaceId: forkWorkspaceId,
4438
4496
  worktreeBranch: forkBranch,
4439
- worktreeBaseRef: recoveredBranch,
4497
+ worktreeBaseRef: recoveredBranch ?? recoveredRevision,
4440
4498
  workspaceKind: "fork",
4441
4499
  sessionPersistence: {
4442
4500
  sessionDir,
@@ -4446,11 +4504,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
4446
4504
  }
4447
4505
  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`);
4448
4506
  }
4507
+ const hasDedicatedWorkspace = Boolean(recoveredBranch || recoveredRevision);
4449
4508
  return {
4450
4509
  ...basePlan,
4451
- workspaceMode: "dedicated_worktree",
4452
- workspaceId: recoveredBranch ? buildAttemptWorkspaceId(claimedTask) : null,
4510
+ workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
4511
+ workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
4453
4512
  worktreeBranch: recoveredBranch,
4513
+ workspaceRevision: recoveredRevision,
4454
4514
  sessionPersistence: {
4455
4515
  sessionDir,
4456
4516
  forkFromSessionPath: resolution.sessionPath
@@ -4458,16 +4518,20 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
4458
4518
  };
4459
4519
  }
4460
4520
  const parentBranch = resolution.producerSlot.workspace?.worktreeBranch ?? null;
4521
+ const parentRevision = parentBranch ? null : await sourceAttemptResolver.findInputRevision({
4522
+ attemptN: continueFrom.attemptN,
4523
+ taskId: continueFrom.taskId
4524
+ });
4461
4525
  if (continueFrom.mode === "fork") {
4462
- if (!parentBranch) throw new ProducerContextResolutionError(`Cannot fork continuation of ${continueFrom.taskId}/${continueFrom.attemptN}: producer slot has no worktree branch to fork from`);
4526
+ 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`);
4463
4527
  const forkWorkspaceId = `fork-${claimedTask.task.id}-attempt-${claimedTask.attemptN}`;
4464
- const forkBranch = buildForkBranch(parentBranch, claimedTask.task.id, claimedTask.attemptN);
4528
+ const forkBranch = buildForkBranch(parentBranch ?? `detached-${continueFrom.taskId.slice(0, 8)}`, claimedTask.task.id, claimedTask.attemptN);
4465
4529
  return {
4466
4530
  ...basePlan,
4467
4531
  workspaceMode: "dedicated_worktree",
4468
4532
  workspaceId: forkWorkspaceId,
4469
4533
  worktreeBranch: forkBranch,
4470
- worktreeBaseRef: parentBranch,
4534
+ worktreeBaseRef: parentBranch ?? parentRevision,
4471
4535
  workspaceKind: "fork",
4472
4536
  sessionPersistence: {
4473
4537
  sessionDir,
@@ -4475,11 +4539,13 @@ async function maybeAttachWarmSlotContext(claimedTask, basePlan, stateDirs, slot
4475
4539
  }
4476
4540
  };
4477
4541
  }
4542
+ const hasDedicatedWorkspace = Boolean(parentBranch || parentRevision);
4478
4543
  return {
4479
4544
  ...basePlan,
4480
- workspaceMode: "dedicated_worktree",
4481
- workspaceId: parentBranch ? buildAttemptWorkspaceId(claimedTask) : null,
4545
+ workspaceMode: hasDedicatedWorkspace ? "dedicated_worktree" : "shared_mount",
4546
+ workspaceId: hasDedicatedWorkspace ? buildAttemptWorkspaceId(claimedTask) : null,
4482
4547
  worktreeBranch: parentBranch,
4548
+ workspaceRevision: parentRevision,
4483
4549
  sessionPersistence: {
4484
4550
  sessionDir,
4485
4551
  forkFromSessionPath: resolution.sessionPath
@@ -5403,15 +5469,11 @@ async function listRegisteredWorktrees(mainWorktree) {
5403
5469
  }
5404
5470
  function isRealPathInsideRoot(path, root) {
5405
5471
  try {
5406
- return isResolvedPathInsideRoot$1(realpathSync(path), realpathSync(root));
5472
+ return isResolvedPathInsideRoot(realpathSync(path), realpathSync(root));
5407
5473
  } catch {
5408
5474
  return false;
5409
5475
  }
5410
5476
  }
5411
- function isResolvedPathInsideRoot$1(path, root) {
5412
- const rel = relative(root, path);
5413
- return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
5414
- }
5415
5477
  function canonicalPath(path) {
5416
5478
  try {
5417
5479
  return realpathSync(path);
@@ -5678,11 +5740,18 @@ function signalExitCode(signal) {
5678
5740
  //#region src/lib/source-attempts.ts
5679
5741
  function createApiSourceAttemptResolver(args) {
5680
5742
  const { agent } = args;
5681
- return { async findOutputBranch(input) {
5682
- const attempt = (await agent.tasks.listAttempts(input.taskId)).find((candidate) => candidate.attemptN === input.attemptN);
5683
- if (!attempt || attempt.status !== "completed") return null;
5684
- return resolveOutputBranch(attempt.output);
5685
- } };
5743
+ return {
5744
+ async findOutputBranch(input) {
5745
+ const attempt = (await agent.tasks.listAttempts(input.taskId)).find((candidate) => candidate.attemptN === input.attemptN);
5746
+ if (!attempt || attempt.status !== "completed") return null;
5747
+ return resolveOutputBranch(attempt.output);
5748
+ },
5749
+ async findInputRevision(input) {
5750
+ const task = await agent.tasks.get(input.taskId);
5751
+ if (task.status !== "completed" || task.acceptedAttemptN !== input.attemptN) return null;
5752
+ return resolveTaskWorkspaceRevision(task.input);
5753
+ }
5754
+ };
5686
5755
  }
5687
5756
  function resolveOutputBranch(output) {
5688
5757
  if (!output || typeof output !== "object") return null;
@@ -6782,10 +6851,6 @@ async function isPathInsideRoot(path, root) {
6782
6851
  }
6783
6852
  return isResolvedPathInsideRoot(realResolvedPath, realResolvedRoot);
6784
6853
  }
6785
- function isResolvedPathInsideRoot(path, root) {
6786
- const rel = relative(root, path);
6787
- return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
6788
- }
6789
6854
  async function computeCompressedSessionFingerprint(path) {
6790
6855
  const hash = createHash("sha256");
6791
6856
  let bytes = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.34.2",
3
+ "version": "0.36.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.",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@earendil-works/gondolin": "^0.9.1",
47
- "@earendil-works/pi-ai": "0.74.0",
47
+ "@earendil-works/pi-ai": "0.79.4",
48
48
  "@earendil-works/pi-coding-agent": "0.79.4",
49
49
  "@opentelemetry/api": "^1.9.0",
50
50
  "@opentelemetry/exporter-trace-otlp-proto": "^0.212.0",
@@ -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/pi-runtime": "0.4.0",
66
- "@themoltnet/sdk": "0.128.0",
67
- "@themoltnet/agent-runtime": "0.39.0"
65
+ "@themoltnet/pi-runtime": "0.6.0",
66
+ "@themoltnet/agent-runtime": "0.40.0",
67
+ "@themoltnet/sdk": "0.128.0"
68
68
  },
69
69
  "devDependencies": {
70
70
  "tsx": "^4.7.0",
@@ -72,10 +72,10 @@
72
72
  "vite": "^8.0.0",
73
73
  "vite-plugin-dts": "^4.5.4",
74
74
  "vitest": "^3.0.0",
75
- "@moltnet/crypto-service": "0.1.0",
76
75
  "@moltnet/bootstrap": "0.1.0",
77
- "@moltnet/observability": "0.1.0",
78
- "@moltnet/tasks": "0.1.0"
76
+ "@moltnet/crypto-service": "0.1.0",
77
+ "@moltnet/tasks": "0.1.0",
78
+ "@moltnet/observability": "0.1.0"
79
79
  },
80
80
  "nx": {
81
81
  "projectType": "application",