@elizaos/autonomous 2.0.0-alpha.65 → 2.0.0-alpha.66

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.
@@ -0,0 +1,3 @@
1
+ {
2
+ "avatarIndex": 1
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/autonomous",
3
- "version": "2.0.0-alpha.65",
3
+ "version": "2.0.0-alpha.66",
4
4
  "description": "Standalone autonomous agent runtime and backend server package for elizaOS.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4166,12 +4166,36 @@ export async function startEliza(
4166
4166
  // (ActionFilterService, EmbeddingGenerationService) race ahead and use
4167
4167
  // the cloud plugin's TEXT_EMBEDDING handler — which hits a paid API —
4168
4168
  // because local-embedding's heavier init hasn't completed yet.
4169
+ // Check whether the embedding model file is already downloaded.
4170
+ // When missing, we defer local-embedding registration to after
4171
+ // runtime.initialize() so the multi-GB download does not block
4172
+ // startup (runtime.initialize → ensureEmbeddingDimension would hang
4173
+ // waiting for the model).
4174
+ let deferLocalEmbedding = false;
4169
4175
  if (localEmbeddingPlugin) {
4170
4176
  configureLocalEmbeddingPlugin(localEmbeddingPlugin.plugin, config);
4171
- await runtime.registerPlugin(localEmbeddingPlugin.plugin);
4172
- logger.info(
4173
- "[eliza] plugin-local-embedding pre-registered (TEXT_EMBEDDING ready)",
4174
- );
4177
+
4178
+ const embeddingModelDir =
4179
+ process.env.MODELS_DIR ??
4180
+ path.join(resolveStateDir(), "models");
4181
+ const embeddingModelFile =
4182
+ process.env.LOCAL_EMBEDDING_MODEL ?? "nomic-embed-text-v1.5.Q5_K_M.gguf";
4183
+ const modelPath = path.join(embeddingModelDir, embeddingModelFile);
4184
+
4185
+ try {
4186
+ await fs.access(modelPath);
4187
+ // Model exists — register now so ensureEmbeddingDimension works
4188
+ await runtime.registerPlugin(localEmbeddingPlugin.plugin);
4189
+ logger.info(
4190
+ "[eliza] plugin-local-embedding pre-registered (TEXT_EMBEDDING ready)",
4191
+ );
4192
+ } catch {
4193
+ deferLocalEmbedding = true;
4194
+ logger.info(
4195
+ `[eliza] Embedding model not yet downloaded (${embeddingModelFile}). ` +
4196
+ "Deferring local-embedding to avoid blocking startup.",
4197
+ );
4198
+ }
4175
4199
  } else {
4176
4200
  logger.warn(
4177
4201
  "[eliza] @elizaos/plugin-local-embedding not found — embeddings " +
@@ -4277,6 +4301,15 @@ export async function startEliza(
4277
4301
  }
4278
4302
  };
4279
4303
 
4304
+ // Keep the event loop alive during the entire startup sequence.
4305
+ // PGLite and the local-embedding model download may `unref()` their
4306
+ // internal handles, causing Node to exit before the initialize() promise
4307
+ // settles when there is nothing else keeping the loop alive (e.g.
4308
+ // server-only mode with stdin set to "ignore"). This guard stays active
4309
+ // until a permanent handle (API server or the serverOnly keepAlive
4310
+ // interval) takes over.
4311
+ const startupKeepAlive = setInterval(() => {}, 1 << 30);
4312
+
4280
4313
  const initializeRuntimeServices = async (): Promise<void> => {
4281
4314
  // 8. Initialize the runtime (registers remaining plugins, starts services)
4282
4315
  await runtime.initialize();
@@ -4343,6 +4376,24 @@ export async function startEliza(
4343
4376
 
4344
4377
  installActionAliases(runtime);
4345
4378
 
4379
+ // Deferred local-embedding registration — register the plugin now that
4380
+ // runtime.initialize() has completed. The model will be downloaded on
4381
+ // first embedding request rather than blocking startup.
4382
+ if (deferLocalEmbedding && localEmbeddingPlugin) {
4383
+ void (async () => {
4384
+ try {
4385
+ await runtime.registerPlugin(localEmbeddingPlugin.plugin);
4386
+ logger.info(
4387
+ "[eliza] plugin-local-embedding registered (deferred — model will download on first use)",
4388
+ );
4389
+ } catch (err) {
4390
+ logger.warn(
4391
+ `[eliza] Deferred local-embedding registration failed: ${formatError(err)}`,
4392
+ );
4393
+ }
4394
+ })();
4395
+ }
4396
+
4346
4397
  // 9. Graceful shutdown handler
4347
4398
  //
4348
4399
  // In headless mode the caller (dev-server / Electron) owns the process
@@ -4406,6 +4457,10 @@ export async function startEliza(
4406
4457
 
4407
4458
  // ── Headless mode — return runtime for API server wiring ──────────────
4408
4459
  if (opts?.headless) {
4460
+ // In headless mode the caller owns the process lifecycle, so the
4461
+ // startupKeepAlive is no longer needed — the caller's own event-loop
4462
+ // handles (bun --watch, Electron, etc.) keep the process alive.
4463
+ clearInterval(startupKeepAlive);
4409
4464
  void loadHooksSystem();
4410
4465
  logger.info(
4411
4466
  "[eliza] Runtime initialised in headless mode (autonomy enabled)",
@@ -4613,7 +4668,9 @@ export async function startEliza(
4613
4668
  logger.info("[eliza] Running in server-only mode (no interactive chat)");
4614
4669
  console.log("[eliza] Server running. Press Ctrl+C to stop.");
4615
4670
 
4616
- // Keep process alive the API server handles all interaction
4671
+ // The startupKeepAlive already keeps the process alive. Replace it
4672
+ // with a permanent interval so the cleanup handler can reference it.
4673
+ clearInterval(startupKeepAlive);
4617
4674
  const keepAlive = setInterval(() => {}, 1 << 30); // ~12 days
4618
4675
 
4619
4676
  // Cleanup on exit
@@ -4634,6 +4691,9 @@ export async function startEliza(
4634
4691
  }
4635
4692
 
4636
4693
  // ── Interactive chat loop ────────────────────────────────────────────────
4694
+ // The readline interface and API server keep the event loop alive from
4695
+ // here on, so the startup guard is no longer needed.
4696
+ clearInterval(startupKeepAlive);
4637
4697
  const agentName = character.name ?? "Eliza";
4638
4698
  const userId = crypto.randomUUID() as UUID;
4639
4699
  // Use `let` so the fallback path can reassign to fresh IDs.
@@ -4843,6 +4903,7 @@ export async function startInCloudMode(
4843
4903
  if (opts?.headless || opts?.serverOnly) {
4844
4904
  // In headless/server mode, start the API server with the cloud proxy.
4845
4905
  // The proxy exposes the same interface the API server needs.
4906
+ clearInterval(startupKeepAlive);
4846
4907
  logger.info(
4847
4908
  `[eliza] Cloud agent connected (headless). Agent: ${proxy.agentName}`,
4848
4909
  );
@@ -4852,6 +4913,7 @@ export async function startInCloudMode(
4852
4913
  }
4853
4914
 
4854
4915
  // Interactive CLI mode — simple chat loop against the cloud agent
4916
+ clearInterval(startupKeepAlive);
4855
4917
  console.log(
4856
4918
  `\n☁️ Connected to cloud agent "${proxy.agentName}" (${agentId})\n`,
4857
4919
  );