@melaya/runner 1.0.81 → 1.0.83

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.
@@ -64,6 +64,30 @@ export async function connect(opts) {
64
64
  console.log(chalk.yellow(` ⚠ Luma browser bridge failed to start: ${e?.message || e}`));
65
65
  }
66
66
  };
67
+ // Background warm-up of the assistant Python env, once per process, so the
68
+ // first assistant chat spawns near-instantly. No-op on a fresh runner (no
69
+ // shared bundle on disk yet) — the real assistant_start installs it then.
70
+ let _assistantPrewarmed = false;
71
+ const _prewarmAssistantEnv = async () => {
72
+ if (_assistantPrewarmed)
73
+ return;
74
+ _assistantPrewarmed = true;
75
+ try {
76
+ const { getLocalSharedVersion } = await import("./sharedVendor.js");
77
+ const localVersion = getLocalSharedVersion();
78
+ if (!localVersion)
79
+ return; // nothing cached yet — let assistant_start do it
80
+ const { ensurePythonEnv } = await import("./pythonEnv.js");
81
+ await ensurePythonEnv(opts.pythonPath, localVersion, (m) => { if (opts.verbose)
82
+ console.log(chalk.gray(` [assistant prewarm] ${m}`)); });
83
+ if (opts.verbose)
84
+ console.log(chalk.gray(" [assistant prewarm] ready"));
85
+ }
86
+ catch (e) {
87
+ if (opts.verbose)
88
+ console.log(chalk.gray(` [assistant prewarm] skipped: ${e?.message || e}`));
89
+ }
90
+ };
67
91
  socket.on("connect", async () => {
68
92
  spinner.succeed(chalk.green("Connected to Melaya"));
69
93
  // Report detected models
@@ -83,6 +107,12 @@ export async function connect(opts) {
83
107
  // effort — failure here means Luma POSTs fall back to aiohttp
84
108
  // (which 403s under the bot rule) but reads still work.
85
109
  await _ensureLumaBridge();
110
+ // Pre-warm the assistant Python env in the BACKGROUND so the first chat
111
+ // message doesn't pay the venv check + one-time NLTK/cert self-heal inline
112
+ // (the biggest chunk of "◆ Assistant session … takes forever to spawn").
113
+ // We warm with the version already on disk so it hits the cache instead of
114
+ // reinstalling; skipped on a fresh runner (no bundle yet) to avoid churn.
115
+ void _prewarmAssistantEnv();
86
116
  console.log(chalk.gray(" Waiting for pipeline runs...\n"));
87
117
  });
88
118
  socket.on("connect_error", (err) => {
@@ -315,6 +345,10 @@ export async function connect(opts) {
315
345
  PYTHONUTF8: "1",
316
346
  ...sslEnv,
317
347
  MEL_RUN_ID: payload.runId,
348
+ // Pipeline name so phone-driving runs can self-register as the phone's
349
+ // active run (arms the on-device working overlay + its Kill button)
350
+ // regardless of launch surface — desktop, native, or assistant.
351
+ MEL_PIPELINE_NAME: payload.pipelineName,
318
352
  // Point agentscope hooks at the REAL server (not the browser's Vite URL).
319
353
  // Convert wss://api.melaya.org → https://api.melaya.org so pushMessage,
320
354
  // registerRun, and other agentscope HTTP calls reach the tRPC server.
package/dist/pythonEnv.js CHANGED
@@ -320,13 +320,21 @@ async function ensureNltkData(onProgress) {
320
320
  onProgress(`(nltk check exited ${code} — Mode B vector RAG ingest may fail with "Resource 'punkt_tab' not found")`);
321
321
  }
322
322
  }
323
+ // One-time-per-process guard for the cache-hit self-heal (NLTK + cert bundle).
324
+ let _selfHealedThisProcess = false;
323
325
  export async function ensurePythonEnv(systemPython, expectedVersion, onProgress = (m) => console.log(chalk.gray(` [venv] ${m}`))) {
324
326
  if (venvIsValid(expectedVersion)) {
325
- // Self-heal: ensure NLTK data is present even when the venv marker
326
- // says we're up to date. This is what previously broke for boxes
327
- // that upgraded the runner without invalidating the marker.
328
- await ensureNltkData(onProgress);
329
- await _resolveAndCacheCertBundle(onProgress);
327
+ // Self-heal: ensure NLTK data + the cert bundle are present even when the
328
+ // venv marker says we're up to date (this is what previously broke for
329
+ // boxes that upgraded the runner without invalidating the marker). Both are
330
+ // stable for the process lifetime, so run them ONCE per runner process — the
331
+ // old code spawned two extra Python processes on EVERY assistant/pipeline
332
+ // start (~0.5-1.5s of pure latency on the assistant-spawn critical path).
333
+ if (!_selfHealedThisProcess) {
334
+ _selfHealedThisProcess = true;
335
+ await ensureNltkData(onProgress);
336
+ await _resolveAndCacheCertBundle(onProgress);
337
+ }
330
338
  return { ok: true, pythonPath: venvPython() };
331
339
  }
332
340
  if (!existsSync(AGENTSCOPE)) {
@@ -7,5 +7,12 @@
7
7
  * runtime from the server's /api/v1/agents/shared-bundle endpoint,
8
8
  * integrity-verified via SHA-256, and cached at ~/.melaya-runner/shared/.
9
9
  */
10
+ /**
11
+ * The shared-bundle version currently installed on disk (from shared-version.txt),
12
+ * or null if none/incomplete. Used to pre-warm the Python env on connect with the
13
+ * SAME version the cache already holds, so the warm-up hits the cache instead of
14
+ * triggering a reinstall.
15
+ */
16
+ export declare function getLocalSharedVersion(): string | null;
10
17
  export declare function getSharedDir(): string;
11
18
  export declare function ensureSharedModules(serverUrl: string, expectedVersion: string, runnerToken: string): Promise<void>;
@@ -14,6 +14,22 @@ import { createHash, createHmac, timingSafeEqual } from "crypto";
14
14
  const CACHE_DIR = join(homedir(), ".melaya-runner");
15
15
  const SHARED_DIR = join(CACHE_DIR, "shared");
16
16
  const VERSION_FILE = join(CACHE_DIR, "shared-version.txt");
17
+ /**
18
+ * The shared-bundle version currently installed on disk (from shared-version.txt),
19
+ * or null if none/incomplete. Used to pre-warm the Python env on connect with the
20
+ * SAME version the cache already holds, so the warm-up hits the cache instead of
21
+ * triggering a reinstall.
22
+ */
23
+ export function getLocalSharedVersion() {
24
+ try {
25
+ if (existsSync(VERSION_FILE) && cacheLooksValid()) {
26
+ const v = readFileSync(VERSION_FILE, "utf-8").trim();
27
+ return v || null;
28
+ }
29
+ }
30
+ catch { /* best-effort */ }
31
+ return null;
32
+ }
17
33
  export function getSharedDir() {
18
34
  // Return the CACHE_DIR root (not shared/ subdir) because the bundle
19
35
  // now includes agentscope/ and crews/ alongside shared/. PYTHONPATH
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.0.81",
3
+ "version": "1.0.83",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,