nexo-brain 7.30.15 → 7.30.17

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.30.15",
3
+ "version": "7.30.17",
4
4
  "description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
5
5
  "author": {
6
6
  "name": "NEXO Brain",
package/README.md CHANGED
@@ -18,7 +18,9 @@
18
18
 
19
19
  [Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
20
20
 
21
- Version `7.30.15` is the current packaged-runtime line. Patch release over v7.30.14 - Email NEXO reply lifecycle closure now handles affirmative actionable replies and prevents already-closed processed threads from reopening.
21
+ Version `7.30.17` is the current packaged-runtime line. Patch release over v7.30.16 - F0.6 repairs promoted helper imports for personal scripts by adding a core-backed compatibility shim without duplicating the script catalog.
22
+
23
+ Previously in `7.30.16`: patch release over v7.30.14 - Desktop diagnostics can read embedding migration status without warming models, and the coordinated Desktop update path is covered for bundled model verification and obsolete managed model cleanup.
22
24
 
23
25
  Previously in `7.30.14`: patch release over v7.30.13 - support tickets, provider capability discovery, task-close rearming, internal audit followups, and the memory-observation watchdog are aligned for Desktop-managed agents.
24
26
 
package/bin/nexo-brain.js CHANGED
@@ -666,6 +666,11 @@ function shouldSkipModelWarmup() {
666
666
  return ["1", "true", "yes", "on"].includes(flag);
667
667
  }
668
668
 
669
+ function shouldInstallLocalClassifierWarmupDeps() {
670
+ const flag = String(process.env.NEXO_LOCAL_CLASSIFIER || "").trim().toLowerCase();
671
+ return ["1", "true", "on", "auto"].includes(flag);
672
+ }
673
+
669
674
  function resolveSystemPython() {
670
675
  return run("which python3") || run("which python") || "python3";
671
676
  }
@@ -705,13 +710,15 @@ function installWarmupPythonDependencies(pythonPath, { quiet = false, installRun
705
710
  }
706
711
  }
707
712
 
708
- const classifierResult = spawnSync(
709
- pythonPath,
710
- [...pipCommon, ...WARMUP_PIP_PACKAGES],
711
- { stdio, timeout: WARMUP_TIMEOUT_MS }
712
- );
713
- if (classifierResult.status !== 0) {
714
- throw new Error("failed to install local classifier dependencies for model warmup");
713
+ if (shouldInstallLocalClassifierWarmupDeps()) {
714
+ const classifierResult = spawnSync(
715
+ pythonPath,
716
+ [...pipCommon, ...WARMUP_PIP_PACKAGES],
717
+ { stdio, timeout: WARMUP_TIMEOUT_MS }
718
+ );
719
+ if (classifierResult.status !== 0) {
720
+ throw new Error("failed to install local classifier dependencies for model warmup");
721
+ }
715
722
  }
716
723
  }
717
724
 
@@ -766,6 +773,157 @@ function runDesktopAwareModelWarmup(pythonPath, nexoHome = NEXO_HOME, options =
766
773
  runMandatoryModelWarmup(pythonPath, nexoHome, options);
767
774
  }
768
775
 
776
+ function slugifyLocalModelName(value) {
777
+ return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
778
+ }
779
+
780
+ function readManagedModelLock(dir) {
781
+ try {
782
+ const lockPath = path.join(dir, ".nexo-model-lock.json");
783
+ if (!fs.existsSync(lockPath)) return null;
784
+ const payload = JSON.parse(fs.readFileSync(lockPath, "utf8"));
785
+ return payload && typeof payload === "object" ? payload : null;
786
+ } catch (_) {
787
+ return null;
788
+ }
789
+ }
790
+
791
+ function isManagedModelRevisionDir(dir, { slug = "", revision = "" } = {}) {
792
+ const lock = readManagedModelLock(dir);
793
+ if (!lock) return false;
794
+ if (!lock.name || !lock.revision || (!lock.model_id && !lock.source_repo)) return false;
795
+ if (slug && slugifyLocalModelName(lock.name) !== slug) return false;
796
+ if (revision && String(lock.revision || "") !== String(revision || "")) return false;
797
+ return true;
798
+ }
799
+
800
+ function sha256File(filePath) {
801
+ return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
802
+ }
803
+
804
+ function cleanupObsoleteRuntimeLlmModels(runtimeModelsDir, manifest, { reason = "install" } = {}) {
805
+ if (String(process.env.NEXO_KEEP_OBSOLETE_LLM_MODELS || "").trim() === "1") {
806
+ log(` Keeping obsolete LLM models by NEXO_KEEP_OBSOLETE_LLM_MODELS=1 (${reason}).`);
807
+ return [];
808
+ }
809
+ if (!fs.existsSync(runtimeModelsDir)) return [];
810
+
811
+ const allowed = new Map();
812
+ for (const spec of manifest.models || []) {
813
+ const slug = slugifyLocalModelName(spec.name || "");
814
+ const revision = String(spec.revision || "").trim();
815
+ if (!slug || !revision) continue;
816
+ if (!allowed.has(slug)) allowed.set(slug, new Set());
817
+ allowed.get(slug).add(revision);
818
+ }
819
+
820
+ const removed = [];
821
+ for (const slugEntry of fs.readdirSync(runtimeModelsDir, { withFileTypes: true })) {
822
+ if (!slugEntry.isDirectory()) continue;
823
+ const slug = slugEntry.name;
824
+ if (slug.startsWith(".")) continue;
825
+ if (slug === "_hf-cache") continue;
826
+ const slugDir = path.join(runtimeModelsDir, slug);
827
+ const allowedRevisions = allowed.get(slug) || new Set();
828
+ for (const revisionEntry of fs.readdirSync(slugDir, { withFileTypes: true })) {
829
+ if (!revisionEntry.isDirectory()) continue;
830
+ const revision = revisionEntry.name;
831
+ const revisionDir = path.join(slugDir, revision);
832
+ if (allowedRevisions.has(revision)) continue;
833
+ if (!isManagedModelRevisionDir(revisionDir, { slug, revision })) continue;
834
+ fs.rmSync(revisionDir, { recursive: true, force: true });
835
+ removed.push(path.relative(runtimeModelsDir, revisionDir));
836
+ }
837
+ try {
838
+ if (fs.readdirSync(slugDir).length === 0) fs.rmdirSync(slugDir);
839
+ } catch (_) {}
840
+ }
841
+
842
+ if (removed.length > 0) {
843
+ log(` Removed ${removed.length} obsolete managed LLM model revision(s) (${reason}).`);
844
+ }
845
+ return removed;
846
+ }
847
+
848
+ function copyBundledLlmModelsToRuntime(nexoHome = NEXO_HOME, {
849
+ reason = "install",
850
+ bundledModelsDir = path.join(__dirname, "..", "models"),
851
+ manifestPath = path.join(__dirname, "..", "src", "local_model_manifest.json"),
852
+ } = {}) {
853
+ // OFFLINE-FIRST: copy bundled LLM models to runtime/models BEFORE warmup,
854
+ // so fastembed finds them locally and skips HuggingFace downloads.
855
+ // Bundle layout: resources/brain-bundle/models/<source-repo-name>/<all files>.
856
+ // Target layout: <NEXO_HOME>/runtime/models/<spec.name slugified>/<revision>/<files>.
857
+ // We map by source_repo basename to match local_model_manifest.json.
858
+ if (!fs.existsSync(bundledModelsDir)) return 0;
859
+ try {
860
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
861
+ const runtimeModelsDir = path.join(nexoHome, "runtime", "models");
862
+ let modelsCopied = 0;
863
+ for (const spec of manifest.models || []) {
864
+ // Bundle layout supports either model_id basename (e.g.
865
+ // "bge-base-en-v1.5" from "BAAI/bge-base-en-v1.5") or source_repo
866
+ // basename (e.g. "bge-base-en-v1.5-onnx-q" from "qdrant/...").
867
+ const modelIdName = (spec.model_id || "").split("/").pop();
868
+ const sourceRepoName = (spec.source_repo || "").split("/").pop();
869
+ let sourceDir = path.join(bundledModelsDir, sourceRepoName);
870
+ if (!fs.existsSync(sourceDir)) {
871
+ sourceDir = path.join(bundledModelsDir, modelIdName);
872
+ }
873
+ if (!fs.existsSync(sourceDir)) continue;
874
+ const slug = slugifyLocalModelName(spec.name || "");
875
+ const targetDir = path.join(runtimeModelsDir, slug, spec.revision);
876
+ fs.mkdirSync(targetDir, { recursive: true });
877
+ const missingFiles = [];
878
+ for (const f of (spec.required_files || [])) {
879
+ const src = path.join(sourceDir, f.path);
880
+ const dst = path.join(targetDir, f.path);
881
+ if (!fs.existsSync(src)) {
882
+ missingFiles.push(f.path);
883
+ continue;
884
+ }
885
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
886
+ let shouldCopy = !fs.existsSync(dst) || (f.size && fs.statSync(dst).size !== f.size);
887
+ if (!shouldCopy && f.sha256 && sha256File(dst) !== f.sha256) {
888
+ shouldCopy = true;
889
+ }
890
+ if (shouldCopy) {
891
+ fs.copyFileSync(src, dst);
892
+ }
893
+ if (f.size && fs.statSync(dst).size !== f.size) {
894
+ missingFiles.push(`${f.path}:size`);
895
+ continue;
896
+ }
897
+ if (f.sha256) {
898
+ const actual = sha256File(dst);
899
+ if (actual !== f.sha256) {
900
+ missingFiles.push(`${f.path}:sha256`);
901
+ }
902
+ }
903
+ }
904
+ if (missingFiles.length) {
905
+ log(` WARN: bundled LLM model ${spec.name} incomplete (${missingFiles.join(", ")})`);
906
+ continue;
907
+ }
908
+ // Write the lock file to match revision (avoids re-download).
909
+ fs.writeFileSync(path.join(targetDir, ".nexo-model-lock.json"), JSON.stringify({
910
+ name: spec.name, kind: spec.kind, model_id: spec.model_id,
911
+ source_repo: spec.source_repo, revision: spec.revision, model_file: spec.model_file,
912
+ required_files: spec.required_files,
913
+ }, null, 2));
914
+ modelsCopied++;
915
+ }
916
+ if (modelsCopied > 0) log(` Copied ${modelsCopied} pre-bundled LLM model(s) (offline, ${reason}).`);
917
+ if (modelsCopied > 0 && modelsCopied === (manifest.models || []).length) {
918
+ cleanupObsoleteRuntimeLlmModels(runtimeModelsDir, manifest, { reason });
919
+ }
920
+ return modelsCopied;
921
+ } catch (err) {
922
+ log(` WARN: bundled models copy failed during ${reason}: ${err.message}`);
923
+ return 0;
924
+ }
925
+ }
926
+
769
927
  async function runWarmupModelsCommand(args) {
770
928
  const dryRun = args.includes("--dry-run");
771
929
  const json = args.includes("--json");
@@ -3216,6 +3374,7 @@ async function runSetup() {
3216
3374
  log(" Python dependencies reconciled.");
3217
3375
  }
3218
3376
 
3377
+ copyBundledLlmModelsToRuntime(NEXO_HOME, { reason: "update" });
3219
3378
  const migPythonForWarmup = findVenvPython(NEXO_HOME) || "python3";
3220
3379
  runDesktopAwareModelWarmup(migPythonForWarmup, NEXO_HOME, { reason: "update", installRuntimeDeps: false });
3221
3380
 
@@ -3492,6 +3651,7 @@ async function runSetup() {
3492
3651
  stampRuntimeRepairBaseline(NEXO_HOME, "bin.nexo-brain.same-version-repair")
3493
3652
  );
3494
3653
 
3654
+ copyBundledLlmModelsToRuntime(NEXO_HOME, { reason: "repair" });
3495
3655
  runDesktopAwareModelWarmup(syncPython, NEXO_HOME, { reason: "repair" });
3496
3656
  logMacPermissionsNotice(NEXO_HOME, syncPython);
3497
3657
 
@@ -3935,71 +4095,7 @@ async function runSetup() {
3935
4095
  }
3936
4096
  log("Dependencies installed.");
3937
4097
 
3938
- // OFFLINE-FIRST: copy bundled LLM models to runtime/models BEFORE warmup,
3939
- // so fastembed finds them locally and skips the ~217MB HuggingFace download.
3940
- // Bundle layout: resources/brain-bundle/models/<source-repo-name>/<all files>.
3941
- // Target layout: <NEXO_HOME>/runtime/models/<spec.name slugified>/<revision>/<files>.
3942
- // We map by source_repo basename to match local_model_manifest.json.
3943
- const bundledModelsDir = path.join(__dirname, "..", "models");
3944
- if (fs.existsSync(bundledModelsDir)) {
3945
- try {
3946
- const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "src", "local_model_manifest.json"), "utf8"));
3947
- const runtimeModelsDir = path.join(NEXO_HOME, "runtime", "models");
3948
- let modelsCopied = 0;
3949
- for (const spec of manifest.models || []) {
3950
- // Bundle layout supports either model_id basename (e.g.
3951
- // "bge-base-en-v1.5" from "BAAI/bge-base-en-v1.5") or source_repo
3952
- // basename (e.g. "bge-base-en-v1.5-onnx-q" from "qdrant/...").
3953
- const modelIdName = (spec.model_id || "").split("/").pop();
3954
- const sourceRepoName = (spec.source_repo || "").split("/").pop();
3955
- let sourceDir = path.join(bundledModelsDir, modelIdName);
3956
- if (!fs.existsSync(sourceDir)) {
3957
- sourceDir = path.join(bundledModelsDir, sourceRepoName);
3958
- }
3959
- if (!fs.existsSync(sourceDir)) continue;
3960
- const slug = (spec.name || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
3961
- const targetDir = path.join(runtimeModelsDir, slug, spec.revision);
3962
- fs.mkdirSync(targetDir, { recursive: true });
3963
- const missingFiles = [];
3964
- for (const f of (spec.required_files || [])) {
3965
- const src = path.join(sourceDir, f.path);
3966
- const dst = path.join(targetDir, f.path);
3967
- if (!fs.existsSync(src)) {
3968
- missingFiles.push(f.path);
3969
- continue;
3970
- }
3971
- fs.mkdirSync(path.dirname(dst), { recursive: true });
3972
- if (!fs.existsSync(dst) || (f.size && fs.statSync(dst).size !== f.size)) {
3973
- fs.copyFileSync(src, dst);
3974
- }
3975
- if (f.size && fs.statSync(dst).size !== f.size) {
3976
- missingFiles.push(`${f.path}:size`);
3977
- continue;
3978
- }
3979
- if (f.sha256) {
3980
- const actual = crypto.createHash("sha256").update(fs.readFileSync(dst)).digest("hex");
3981
- if (actual !== f.sha256) {
3982
- missingFiles.push(`${f.path}:sha256`);
3983
- }
3984
- }
3985
- }
3986
- if (missingFiles.length) {
3987
- log(` WARN: bundled LLM model ${spec.name} incomplete (${missingFiles.join(", ")})`);
3988
- continue;
3989
- }
3990
- // Write the lock file to match revision (avoids re-download).
3991
- fs.writeFileSync(path.join(targetDir, ".nexo-model-lock.json"), JSON.stringify({
3992
- name: spec.name, kind: spec.kind, model_id: spec.model_id,
3993
- source_repo: spec.source_repo, revision: spec.revision, model_file: spec.model_file,
3994
- required_files: spec.required_files,
3995
- }, null, 2));
3996
- modelsCopied++;
3997
- }
3998
- if (modelsCopied > 0) log(` Copied ${modelsCopied} pre-bundled LLM model(s) (offline).`);
3999
- } catch (err) {
4000
- log(` WARN: bundled models copy failed: ${err.message}`);
4001
- }
4002
- }
4098
+ copyBundledLlmModelsToRuntime(NEXO_HOME, { reason: "install" });
4003
4099
 
4004
4100
  runDesktopAwareModelWarmup(python, NEXO_HOME, { reason: "install", installRuntimeDeps: false });
4005
4101
 
@@ -5121,4 +5217,13 @@ if (isCliEntrypoint()) {
5121
5217
  console.error("Setup failed:", err.message);
5122
5218
  process.exit(1);
5123
5219
  });
5220
+ } else {
5221
+ module.exports = {
5222
+ cleanupObsoleteRuntimeLlmModels,
5223
+ copyBundledLlmModelsToRuntime,
5224
+ isManagedModelRevisionDir,
5225
+ readManagedModelLock,
5226
+ sha256File,
5227
+ slugifyLocalModelName,
5228
+ };
5124
5229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "7.30.15",
3
+ "version": "7.30.17",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
5
  "description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
6
6
  "homepage": "https://nexo-brain.com",
@@ -1211,6 +1211,15 @@ def _local_classifier_install_command() -> list[str]:
1211
1211
  return cmd
1212
1212
 
1213
1213
 
1214
+ def _local_classifier_auto_install_enabled() -> bool:
1215
+ return str(os.environ.get("NEXO_LOCAL_CLASSIFIER", "")).strip().lower() in {
1216
+ "1",
1217
+ "true",
1218
+ "on",
1219
+ "auto",
1220
+ }
1221
+
1222
+
1214
1223
  def _install_local_classifier_worker() -> None:
1215
1224
  from classifier_local import MODEL_REVISION
1216
1225
 
@@ -1311,6 +1320,18 @@ def _maybe_install_local_classifier() -> None:
1311
1320
  "opt_out": True,
1312
1321
  })
1313
1322
  return
1323
+ if not _local_classifier_auto_install_enabled():
1324
+ from classifier_local import MODEL_REVISION
1325
+
1326
+ _write_classifier_install_log("[classifier-install] deferred: model is not bundled; set NEXO_LOCAL_CLASSIFIER=auto to install")
1327
+ _write_classifier_install_state({
1328
+ "installed_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
1329
+ "model_revision": MODEL_REVISION,
1330
+ "deps_ok": False,
1331
+ "deferred": True,
1332
+ "reason": "not_bundled",
1333
+ })
1334
+ return
1314
1335
  try:
1315
1336
  deps_ok, versions, _missing = _probe_local_classifier_dependencies()
1316
1337
  if deps_ok:
@@ -3133,6 +3154,103 @@ def _ensure_f06_legacy_shims() -> None:
3133
3154
  paths.finalize_backup_snapshot(conflict_root)
3134
3155
 
3135
3156
 
3157
+ def _ensure_f06_personal_script_import_shims() -> None:
3158
+ """Expose promoted core helpers to legacy personal scripts.
3159
+
3160
+ Some operator-owned scripts are executed directly from
3161
+ ``NEXO_HOME/personal/scripts`` and therefore only get that directory on
3162
+ ``sys.path``. When Block E.6 promoted ``nexo_personal_automation.py`` into
3163
+ ``core/scripts``, those direct scripts lost the bare import
3164
+ ``from nexo_personal_automation import ...``. Keep the source of truth in
3165
+ core, but add a tiny compatibility entry in personal/scripts.
3166
+ """
3167
+
3168
+ helper_names = ("nexo_personal_automation.py",)
3169
+ core_scripts = NEXO_HOME / "core" / "scripts"
3170
+ personal_scripts = NEXO_HOME / "personal" / "scripts"
3171
+ stub_marker = "# Auto-generated by NEXO F0.6 personal import shim."
3172
+
3173
+ def _same_file(a: Path, b: Path) -> bool:
3174
+ try:
3175
+ return a.is_file() and b.is_file() and a.read_bytes() == b.read_bytes()
3176
+ except Exception:
3177
+ return False
3178
+
3179
+ def _managed_stub(path: Path) -> bool:
3180
+ try:
3181
+ return path.is_file() and path.read_text(encoding="utf-8", errors="ignore").startswith(stub_marker)
3182
+ except Exception:
3183
+ return False
3184
+
3185
+ def _write_stub(shim: Path, target: Path) -> None:
3186
+ relative = os.path.relpath(str(target), str(shim.parent))
3187
+ shim.write_text(
3188
+ f"""{stub_marker}
3189
+ from __future__ import annotations
3190
+
3191
+ import importlib.util as _importlib_util
3192
+ from pathlib import Path as _Path
3193
+
3194
+ _TARGET = (_Path(__file__).resolve().parent / {relative!r}).resolve()
3195
+ _SPEC = _importlib_util.spec_from_file_location("_nexo_core_personal_automation", _TARGET)
3196
+ if _SPEC is None or _SPEC.loader is None:
3197
+ raise ImportError(f"Cannot load NEXO core helper at {{_TARGET}}")
3198
+ _MODULE = _importlib_util.module_from_spec(_SPEC)
3199
+ _SPEC.loader.exec_module(_MODULE)
3200
+ __all__ = getattr(_MODULE, "__all__", [name for name in vars(_MODULE) if not name.startswith("_")])
3201
+ globals().update({{name: getattr(_MODULE, name) for name in __all__}})
3202
+ """,
3203
+ encoding="utf-8",
3204
+ )
3205
+
3206
+ try:
3207
+ personal_scripts.mkdir(parents=True, exist_ok=True)
3208
+ except Exception as exc:
3209
+ _log(f"[F0.6 shim] could not create personal/scripts for import shims: {exc}")
3210
+ return
3211
+
3212
+ for name in helper_names:
3213
+ target = core_scripts / name
3214
+ if not target.is_file():
3215
+ continue
3216
+ shim = personal_scripts / name
3217
+
3218
+ if shim.is_symlink():
3219
+ try:
3220
+ if shim.resolve(strict=False) == target.resolve(strict=False):
3221
+ continue
3222
+ except Exception:
3223
+ pass
3224
+ try:
3225
+ shim.unlink()
3226
+ except Exception as exc:
3227
+ _log(f"[F0.6 shim] could not replace personal import symlink {name}: {exc}")
3228
+ continue
3229
+
3230
+ if shim.exists():
3231
+ if shim.is_file() and (_same_file(shim, target) or _managed_stub(shim)):
3232
+ try:
3233
+ shim.unlink()
3234
+ except Exception as exc:
3235
+ _log(f"[F0.6 shim] could not replace personal import copy {name}: {exc}")
3236
+ continue
3237
+ else:
3238
+ _log(f"[F0.6 shim] preserving distinct personal import helper {shim}")
3239
+ continue
3240
+
3241
+ try:
3242
+ relative = os.path.relpath(str(target), str(shim.parent))
3243
+ shim.symlink_to(relative)
3244
+ continue
3245
+ except Exception as exc:
3246
+ _log(f"[F0.6 shim] symlink create failed for personal import helper {name}: {exc}")
3247
+
3248
+ try:
3249
+ _write_stub(shim, target)
3250
+ except Exception as exc:
3251
+ _log(f"[F0.6 shim] stub create failed for personal import helper {name}: {exc}")
3252
+
3253
+
3136
3254
  def _rewrite_f06_launch_agents() -> int:
3137
3255
  """Rewrite lingering LaunchAgent paths to canonical F0.6 locations."""
3138
3256
  import re as _re
@@ -3258,6 +3376,7 @@ def _maybe_migrate_to_f06_layout() -> None:
3258
3376
  _promote_packaged_runtime_code_to_core()
3259
3377
  if _f06_live_legacy_paths():
3260
3378
  _ensure_f06_legacy_shims()
3379
+ _ensure_f06_personal_script_import_shims()
3261
3380
  _cleanup_f06_root_residue()
3262
3381
  try:
3263
3382
  _rewrite_f06_launch_agents()
@@ -3477,6 +3596,7 @@ def _maybe_migrate_to_f06_layout() -> None:
3477
3596
  except Exception as e:
3478
3597
  _log(f"[F0.6] marker write failed: {e}")
3479
3598
  _ensure_f06_legacy_shims()
3599
+ _ensure_f06_personal_script_import_shims()
3480
3600
  _cleanup_f06_root_residue()
3481
3601
  try:
3482
3602
  rewritten = _rewrite_f06_launch_agents()
@@ -17,6 +17,8 @@ from cognitive._core import (
17
17
  _get_db, _init_tables, _migrate_lifecycle, _migrate_co_activation,
18
18
  _migrate_memory_personalization,
19
19
  _auto_migrate_embeddings,
20
+ embedding_migration_status,
21
+ _active_embedding_context, _row_embedding_array, _embedding_migration_uses_shadow,
20
22
  _get_model, _get_reranker, rerank_results,
21
23
  embed, cosine_similarity, _array_to_blob, _blob_to_array,
22
24
  extract_temporal_date, redact_secrets,