nexo-brain 7.23.13 → 7.25.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/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -11
- package/bin/nexo-brain.js +42 -235
- package/package.json +1 -1
- package/src/auto_update.py +30 -0
- package/src/automation_supervisor.py +1 -1
- package/src/cli.py +255 -9
- package/src/cognitive_control_observatory.py +224 -0
- package/src/crons/manifest.json +13 -0
- package/src/dashboard/app.py +26 -9
- package/src/db/__init__.py +2 -0
- package/src/db/_fts.py +38 -8
- package/src/db/_learnings.py +1 -1
- package/src/db/_memory_v2.py +107 -1
- package/src/db/_protocol.py +2 -2
- package/src/db/_reminders.py +132 -4
- package/src/db/_schema.py +48 -2
- package/src/doctor/providers/runtime.py +69 -0
- package/src/events_bus.py +4 -5
- package/src/learning_resolver.py +419 -0
- package/src/lifecycle_events.py +9 -9
- package/src/local_context/api.py +67 -5
- package/src/local_context/usage_events.py +24 -0
- package/src/memory_fabric.py +536 -0
- package/src/memory_observation_processor.py +28 -0
- package/src/memory_retrieval.py +5 -5
- package/src/operator_language.py +2 -0
- package/src/plugins/backup.py +1 -1
- package/src/plugins/cortex.py +21 -21
- package/src/plugins/episodic_memory.py +11 -11
- package/src/plugins/goal_engine.py +3 -3
- package/src/plugins/personal_scripts.py +75 -0
- package/src/plugins/protocol.py +10 -1
- package/src/pre_answer_router.py +120 -3
- package/src/r_catalog.py +4 -5
- package/src/saved_not_used_audit.py +31 -31
- package/src/script_registry.py +444 -1
- package/src/scripts/deep-sleep/apply_findings.py +79 -17
- package/src/scripts/nexo-backup.sh +30 -0
- package/src/scripts/nexo-daily-self-audit.py +46 -13
- package/src/scripts/nexo-email-migrate-config.py +2 -2
- package/src/scripts/nexo-email-monitor.py +19 -19
- package/src/scripts/nexo-followup-hygiene.py +40 -8
- package/src/scripts/nexo-followup-runner.py +31 -31
- package/src/scripts/nexo-inbox-hook.sh +1 -1
- package/src/scripts/nexo-learning-validator.py +24 -3
- package/src/scripts/nexo-memory-fabric.py +45 -0
- package/src/server.py +73 -1
- package/src/system_catalog.py +31 -31
- package/src/tools_learnings.py +96 -65
- package/src/tools_memory_v2.py +2 -2
- package/src/tools_sessions.py +25 -7
- package/src/tools_transcripts.py +50 -8
- package/src/transcript_index.py +105 -2
- package/src/transcript_utils.py +65 -13
- package/templates/core-prompts/postmortem-consolidator.md +3 -3
- package/templates/core-prompts/r17-promise-debt-injection.md +1 -1
- package/templates/core-prompts/server-mcp-instructions.md +6 -6
- package/tool-enforcement-map.json +143 -13
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.25.0",
|
|
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,11 @@
|
|
|
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.
|
|
21
|
+
Version `7.25.0` is the current packaged-runtime line. Minor release over v7.24.0 - Memory Fabric links transcript lookup, historical backup diary recovery, unified search and knowledge graph evidence so memories are not available only inside expiring snapshots.
|
|
22
|
+
|
|
23
|
+
Previously in `7.24.0`: minor release over v7.23.13 - Home Agents, cognitive quality controls, English operational copy, and non-blocking task-open context are integrated into main.
|
|
24
|
+
|
|
25
|
+
Previously in `7.23.13`: patch over v7.23.12 - release guardrails now audit publish workflows for masked failures and add minimal-delta coverage for punctual UI edits.
|
|
22
26
|
|
|
23
27
|
Previously in `7.23.12`: patch over v7.23.11 - protected database recovery now repairs degraded Brain tables from backup without rolling back newer rows.
|
|
24
28
|
|
|
@@ -204,24 +208,24 @@ Previously in `7.4.1`: patch release correcting the over-promise in v7.4.0's rel
|
|
|
204
208
|
|
|
205
209
|
Previously in `7.2.0`: minor release consolidating three parallel workstreams into a single Guardian-active-by-default train. Block K roadmap closure (G1 enforcer active, G3 SSH remote-write detector, `src/guardian_runtime_config.py` resolver, `_persist_guardian_hard_defaults` during `nexo update`). F0.6 hardening wave (`nexo rollback f06` CLI, `src/scripts/prune_runtime_backups.py` promoted to core, `docs/f06-layout-contract.md`, three new doctor boot-tier checks, `scripts/nexo-migrate-nora.sh` + `scripts/f0-safe-apply-remote.sh` idempotent migration). Adaptive weights flipped from "14-day calendar wait" to "14 days OR (≥200 samples AND ≥2 days)" with auto-promotion during `nexo update`. Small-fixes batch: R34 `bool("unknown")==True` fix, `classify_scripts_dir` dedup, B10 module-level path constants lazy-evaluated, schedule override audit log, `scripts/pre-release-verify.sh` + `docs/release-discipline.md`, pre-commit hook that blocks commits when `tool-enforcement-map.json` drifts from `src/plugins/`.
|
|
206
210
|
|
|
207
|
-
Previously in `7.1.10`: follow-up over v7.1.8 that shipped two rescue batches of WIP stashed aside during the v7.1.8 release window. First rescue: `src/autonomy_mandate.py` expanded the mandate-detection
|
|
211
|
+
Previously in `7.1.10`: follow-up over v7.1.8 that shipped two rescue batches of WIP stashed aside during the v7.1.8 release window. First rescue: `src/autonomy_mandate.py` expanded the Spanish-language mandate-detection phrase set (do everything / do not stop / you are in charge / I leave you in charge / keep going / do the full plan), added three honest flags on `MandateState` (`execute_until_blocker`, `suppress_mid_task_menus`, `revalidate_after_compaction`) with session filtering, wired post/pre-compact hooks that read those flags, surfaced them through protocol/workflow handlers and session payload, and introduced the new `src/checkpoint_policy.py` module with tests. Second rescue: `scripts/verify_release_readiness.py` gained a smoke-artifact contract pass that validates `release-contracts/smoke/v<version>.json` before any tag push, the release-final audit skill references the new contract, `src/hook_guardrails.py` + `src/hooks/post_tool_use.py` refine the post-tool protocol reminder path with a new contract test, and a couple of core prompts (task-close evidence, r14 correction learning) got wording polish.
|
|
208
212
|
|
|
209
213
|
Previously in `7.1.8`: batch release over v7.1.7 consolidating the Block K Guardian/Enforcer roadmap (auto-drain of stale `protocol_debt` rows, destructive-command pre-tool gate, `guard_check`-required gate, inline guard ack on `nexo_task_open`, Guardian Health in the morning briefing) with Block D hardcode cleanup (classifier-backed `backfill_task_owner`, migration v50 supersedes the duplicate NEXO-product learning pair, new semantic-hardcodes audit) and Block E product guards (LaunchAgent plist protection, agent-name fallbacks no longer leak the product identity, `francisco_emails` removed from the email-config dict export, `runner-health-check.py` + `nexo_personal_automation.py` promoted from personal to core).
|
|
210
214
|
|
|
211
|
-
Previously in `7.0.1`: hotfix over v7.0.0 (db._core.DB_PATH was only caller still hardcoded to legacy ~/.nexo/data/nexo.db; every shared-DB command silently returned empty results post-migration). Previously in `7.0.0`: **BREAKING — Plan
|
|
215
|
+
Previously in `7.0.1`: hotfix over v7.0.0 (db._core.DB_PATH was only caller still hardcoded to legacy ~/.nexo/data/nexo.db; every shared-DB command silently returned empty results post-migration). Previously in `7.0.0`: **BREAKING — Consolidated Plan Phase F0.6**: physical separation of the runtime tree into `~/.nexo/{core,personal,runtime}/`. The flat layout (`~/.nexo/scripts/`, `brain/`, `data/`, `operations/`, ...) is gone. Operators on v6.x are auto-migrated on first `nexo update`; fresh installs land directly in the new tree. New `paths.py` helpers are transition-aware.
|
|
212
216
|
|
|
213
|
-
Previously in `6.5.0`: Plan
|
|
217
|
+
Previously in `6.5.0`: Consolidated Plan Phase F0.2: operators can now `nexo scripts enable|disable|status <name>` any personal automation. The cron wrapper honours the flag at every tick (`exit 0` with `summary='[disabled]'` while the LaunchAgent stays loaded). The companion NEXO Desktop client (a closed-source product, distributed separately) wires the same toggle into its Automations panel. See [CHANGELOG](CHANGELOG.md) for the full diff.
|
|
214
218
|
|
|
215
219
|
> **About NEXO Desktop.** NEXO Desktop is a separate closed-source companion app distributed at [nexo-desktop.com](https://nexo-desktop.com/) — its source does not live in this repo. When release notes mention Desktop they describe a coordinated client release that consumes the Brain's CLI / MCP contract; the Brain itself is fully usable on its own (terminal, Codex, Claude Code, or any MCP client). If you want the product edition rather than the open-source Brain alone, contact `info@wazion.com` and ask about NEXO Desktop.
|
|
216
220
|
|
|
217
221
|
|
|
218
|
-
Previously in `6.4.0`: Plan
|
|
222
|
+
Previously in `6.4.0`: Consolidated Plan Phase F1 — multi-tenant email accounts (`email_accounts` table, `nexo email setup` interactive wizard, `nexo email add --password-stdin --json` for machine consumers, idempotent migrator from legacy `~/.nexo/nexo-email/config.json`). On post-F0.6 installs that legacy-looking path is only a compatibility alias/shim into `~/.nexo/runtime/nexo-email/config.json`; it should never be treated as a second source of truth.
|
|
219
223
|
|
|
220
224
|
Previously in `6.3.1`: privacy hotfix over v6.3.0. The nightly auditor caught that `src/presets/entities_universal.json` in v6.3.0 shipped operator-specific `vhost_mapping` entries (private IPs, hostnames, tenant names). v6.3.1 pulls those out into `src/presets/entities_local.sample.json` (template) + `.gitignore`'d `~/.nexo/brain/presets/entities_local.json` (operator copy), and the installer drops the sample at `nexo init`. No behaviour change on the Guardian side.
|
|
221
225
|
|
|
222
|
-
Previously in `6.3.0` — Plan
|
|
226
|
+
Previously in `6.3.0` — Consolidated Plan wave 2, coordinated with NEXO Desktop v0.18.0. Closes the remaining Guardian roadmap items that do not require an invasive structure migration: extended `cognitive_sentiment` shape (is_correction/valence/intent), extended `entities` schema, 21 labelled rule fixtures with R13 spike gates, Phase F telemetry loops + Deep Sleep phase, pinned local zero-shot classifier skeleton (mDeBERTa), hook respects `NEXO_MIGRATING=1`, `origin` column on `personal_scripts`, and the T4 LLM gate wrapping R15/R23e/R23f/R23h (byte-parity Py ↔ JS). Two pre-release auditors flagged a CRITICAL in the first JS wire (method-name + async mismatch) and a HIGH (classifier bool conflated "no" with "unparseable"); both corrected with regression tests before merge.
|
|
223
227
|
|
|
224
|
-
Previously in `6.1.1`: small fix to `nexo --help` so the `Latest: vX` line reliably appears when NEXO Desktop invokes the CLI via subprocess — unblocks the Desktop Brain auto-update banner that previously couldn't parse the version delta. No behaviour change for interactive terminal users; the 6-hour registry cache still rate-limits network calls. Bundles all v6.1.0 Protocol Enforcer
|
|
228
|
+
Previously in `6.1.1`: small fix to `nexo --help` so the `Latest: vX` line reliably appears when NEXO Desktop invokes the CLI via subprocess — unblocks the Desktop Brain auto-update banner that previously couldn't parse the version delta. No behaviour change for interactive terminal users; the 6-hour registry cache still rate-limits network calls. Bundles all v6.1.0 Protocol Enforcer Phase 2 + multi-claude-sid hotfix content.
|
|
225
229
|
|
|
226
230
|
Previously in `6.0.2`: adds the reserved caller prefix `personal/*` so scripts living in `~/.nexo/scripts/` can invoke the automation backend with their own caller id without editing `src/resonance_map.py`. New kwarg `tier` (`"maximo"` / `"alto"` / `"medio"` / `"bajo"`) on `run_automation_prompt`, `run_automation_interactive`, `nexo_helper.run_automation_text`, `nexo_helper.run_automation_json`, and `nexo-agent-run.py --tier`. Precedence for `personal/*` callers: explicit `tier=` → explicit `reasoning_effort=` → `calibration.preferences.default_resonance` → `DEFAULT_RESONANCE` (`alto`). Registered callers keep their behaviour unchanged. New guide: [`docs/personal-scripts-guide.md`](docs/personal-scripts-guide.md).
|
|
227
231
|
|
|
@@ -235,11 +239,11 @@ Previously in `5.10.1`: silent, one-shot migration that recovers legacy `reasoni
|
|
|
235
239
|
|
|
236
240
|
Previously in `5.10.0`: fixes the deep-sleep extract bloat that made Session 1 take ~57 minutes on some installs (new `bare_mode` on `run_automation_prompt` wires `claude --bare` for JSON-only extractor callers — ~4.3× faster per child, sourced from `ANTHROPIC_API_KEY` env or `~/.claude/anthropic-api-key.txt`). `caller=` is now **mandatory** on `run_automation_prompt` — no silent fallback; every automation subprocess traces back to a registered caller with a deliberate tier. Five personal scripts (`personal/email-monitor`, `personal/github-monitor`, `personal/post-x`, `personal/followup-runner`, `personal/orchestrator-v2`) joined the resonance map with tiers picked per caller based on what each one does. gbp/* marketing posts bumped from `medio` to `alto` (public-facing copy, quality first over speed). 65 legacy protocol debts bulk-resolved as part of the audit — the patterns that generated them are structurally closed by mandatory `caller=` + unified session log + bare_mode.
|
|
237
241
|
|
|
238
|
-
Previously in `5.9.1`: adds `default_resonance` to `brain/calibration.json` via the Desktop-facing schema (`nexo schema --json`), so NEXO Desktop's Preferences dialog renders a
|
|
242
|
+
Previously in `5.9.1`: adds `default_resonance` to `brain/calibration.json` via the Desktop-facing schema (`nexo schema --json`), so NEXO Desktop's Preferences dialog renders a localized resonance selector automatically — no Desktop release needed. `resolve_tier_for_caller` reads calibration first and falls back to the legacy `schedule.json` location. `nexo preferences --resonance` writes both. The UI control only affects interactive sessions (`nexo chat`, Desktop new conversation, interactive `nexo update`); crons and background processes stay pinned per caller in `resonance_map.py`.
|
|
239
243
|
|
|
240
244
|
Previously in `5.9.0`: every Claude/Codex invocation now flows through a central **resonance map** and a **unified session log**. Four tiers (`MAXIMO` / `ALTO` / `MEDIO` / `BAJO`) each resolve to a concrete `(model, reasoning_effort)` pair per backend. User-facing callers (`nexo chat`, Desktop new conversation, interactive `nexo update`) honour the user's `default_resonance` preference; system-owned callers (deep-sleep, evolution, catchup, GBP posts, …) run at a fixed tier chosen per caller in `src/resonance_map.py` — the user's preference never downgrades a cron we decided needs `MAXIMO`. Unknown callers raise `UnregisteredCallerError`. Migration #41 adds `caller`, `session_type`, `started_at`, `ended_at`, `pid`, `resonance_tier` to `automation_runs`; interactive sessions record a row at spawn (with `ended_at=NULL`) and update it on close, so the Brain now has a single source of truth for every Claude/Codex call regardless of origin. New `nexo preferences --resonance` CLI. New MCP tools `nexo_session_log_create` / `nexo_session_log_close` let NEXO Desktop (which spawns `claude` directly from its TypeScript process) feed the same log.
|
|
241
245
|
|
|
242
|
-
Previously in `5.8.2`: the Brain core no longer auto-classifies `followups` and `reminders` on behalf of agents. v5.8.0's `classify_task()` heuristic (NEXO-specific ID prefixes `NF-PROTOCOL-*` / `NF-DS-*` / `NF-AUDIT-*`, Spanish user-
|
|
246
|
+
Previously in `5.8.2`: the Brain core no longer auto-classifies `followups` and `reminders` on behalf of agents. v5.8.0's `classify_task()` heuristic (NEXO-specific ID prefixes `NF-PROTOCOL-*` / `NF-DS-*` / `NF-AUDIT-*`, Spanish user-verb patterns, and agent keywords such as monitor or daily audit) was fine for NEXO's own DB but bled convention into every third-party agent plugged into the shared Brain. The core now persists `internal=0` and `owner=NULL` when the caller omits them, and clients that want automatic classification (NEXO Desktop does, via its `_legacyClassifyOwner` helpers) compute it themselves and pass the result. Migration #40 keeps the columns + indexes; rows already backfilled by v5.8.0 keep their values. `normalise_owner` still explicitly rejects the string `"nexo"` so legacy hardcoding cannot sneak back in.
|
|
243
247
|
|
|
244
248
|
Previously in `5.8.1`: closes a self-reinforcing `launchctl kickstart -k` loop in the watchdog that wedged deep-sleep Phase 2 between 2026-04-14 and 2026-04-17. The cron wrapper now INSERTs an in-flight row (`ended_at=NULL`) at start and traps SIGTERM/INT/HUP to close it with `exit_code=143` instead of vanishing from `cron_runs`. The watchdog interprets in-flight rows as "currently running" and only re-executes after verifying the worker process is dead. `extract.py` classifies CLI failures into transient (`overloaded_error`, rate-limit, timeout, signal — retried next run) and deterministic (skipped after `MAX_POISON_ATTEMPTS`), and passes a slim shared-context (200 head lines + metadata) instead of the full 400+ KB dump. A new `auto_update._heal_deep_sleep_runtime()` repairs existing installs silently on the next `nexo update`: poisoned checkpoints, stale locks, dangling `cron_runs` rows, and bloated `.watchdog-fails` counters.
|
|
245
249
|
|
|
@@ -338,8 +342,8 @@ Version `5.2.1` fixes the Deep Sleep datetime regression and closes the decision
|
|
|
338
342
|
|
|
339
343
|
Version `5.2.0` closes two focused gaps in the Cortex layer that were left open by the v5.1 audit — the high-stakes response-contract detector was English-only, and the `nexo-cortex-cycle` cron was writing a quality snapshot that no reader ever consumed:
|
|
340
344
|
|
|
341
|
-
- `HIGH_STAKES_KEYWORDS_ES` adds ~45 Spanish keywords to the high-stakes detector with accented and unaccented variants, so a goal written in Spanish
|
|
342
|
-
- `NEGATION_PATTERNS` suppresses false positives when the user explicitly disclaims touching the sensitive area
|
|
345
|
+
- `HIGH_STAKES_KEYWORDS_ES` adds ~45 Spanish-language keywords to the high-stakes detector with accented and unaccented variants, so a goal written in Spanish trips the same gate as its English twin.
|
|
346
|
+
- `NEGATION_PATTERNS` suppresses false positives when the user explicitly disclaims touching the sensitive area, including both Spanish and English boundary statements. The raw keyword being present is no longer enough to flag the task.
|
|
343
347
|
- `evaluate_response_confidence` accepts two new optional kwargs, `pre_action_context_hits` (+up to 10) and `area_has_atlas_entry` (+5), so the score can finally reward tasks that loaded real context instead of only punishing unprepared ones. Both signals are capped and cannot override a real risk penalty.
|
|
344
348
|
- A monotonic numeric safeguard layers on top of the boolean decision tree: `answer` downgrades to `verify` when `final_score < 50`, and `verify` downgrades to `defer` when `high_stakes` and `final_score < 30`. The safeguard can only make response discipline stricter, never looser.
|
|
345
349
|
- `handle_cortex_quality` in `src/plugins/cortex.py` now reads `$NEXO_HOME/operations/cortex-quality-latest.json` when the requested window (7 or 1 days) is fresh (<6h 30m) and the schema matches — silent fallback to the live SQL computation on any failure. The handler's JSON response now includes `"source": "cache" | "live"` for observability.
|
package/bin/nexo-brain.js
CHANGED
|
@@ -535,7 +535,7 @@ function isOnboardingComplete(calibration) {
|
|
|
535
535
|
if (meta.onboarding_completed === true) {
|
|
536
536
|
// v7.12.11 — bug surfaced on Inma's smoke install 2026-05-03: Desktop
|
|
537
537
|
// bootstrap runs nexo-brain in --yes/--skip mode, which used to write
|
|
538
|
-
// `onboarding_completed: true` alongside the placeholder "
|
|
538
|
+
// `onboarding_completed: true` alongside the placeholder "User"/"en"
|
|
539
539
|
// defaults. The Desktop wizard then never fired because this returned
|
|
540
540
|
// true on the very first launch. Treat a placeholder name as "marker is
|
|
541
541
|
// a lie, real onboarding never happened" so the renderer can still
|
|
@@ -1918,29 +1918,6 @@ function persistClaudeCliPath(claudePath) {
|
|
|
1918
1918
|
}
|
|
1919
1919
|
|
|
1920
1920
|
function clientSetupStrings(lang) {
|
|
1921
|
-
if (lang === "es") {
|
|
1922
|
-
return {
|
|
1923
|
-
title: "Shared brain siempre activo. Ahora elige clientes y backend de automatización.",
|
|
1924
|
-
detected: "Clientes detectados",
|
|
1925
|
-
yes: "sí",
|
|
1926
|
-
no: "no",
|
|
1927
|
-
useClaudeCodeQ: " ¿Quieres usar Claude Code como cliente interactivo? (recomendado)",
|
|
1928
|
-
useCodexQ: " ¿Quieres usar Codex como cliente interactivo?",
|
|
1929
|
-
useDesktopQ: " ¿Quieres conectar Claude Desktop al mismo brain?",
|
|
1930
|
-
defaultTerminalQ: " ¿Qué cliente debe abrir `nexo chat` por defecto?",
|
|
1931
|
-
automationQ: " ¿Quieres automatización en background? (sleep, deep-sleep, synthesis, self-audit, evolution, postmortem)",
|
|
1932
|
-
automationBackendQ: " ¿Qué backend debe ejecutar esa automatización?",
|
|
1933
|
-
installClaudeQ: " Claude Code no está instalado. ¿Quieres instalarlo ahora?",
|
|
1934
|
-
installCodexQ: " Codex no está instalado. ¿Quieres instalarlo ahora?",
|
|
1935
|
-
installingClaude: "Instalando Claude Code...",
|
|
1936
|
-
installingCodex: "Instalando Codex...",
|
|
1937
|
-
desktopManual: "Claude Desktop no se instala desde NEXO. Cuando exista, se conectará con la sync de clientes.",
|
|
1938
|
-
terminalFallback: (label) => `El cliente terminal elegido no está disponible. \`nexo chat\` quedará pendiente hasta instalar ${label}.`,
|
|
1939
|
-
automationDisabled: (label) => `El backend ${label} sigue sin estar disponible. Se desactiva la automatización por ahora.`,
|
|
1940
|
-
summary: (defaultClient, defaultProfile, backend, backendProfile, automationEnabled) =>
|
|
1941
|
-
`Configuración clientes: chat=${defaultClient}(${defaultProfile}), automation=${automationEnabled ? `${backend}(${backendProfile})` : "none"}`,
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
1921
|
return {
|
|
1945
1922
|
title: "Shared brain is always on. Now choose your clients and automation backend.",
|
|
1946
1923
|
detected: "Detected clients",
|
|
@@ -2021,15 +1998,13 @@ function formatRuntimeProfile(profile = {}) {
|
|
|
2021
1998
|
// tier (maximo / alto / medio / bajo) and that choice drives every backend
|
|
2022
1999
|
// via src/resonance_tiers.json. No more model or effort questions.
|
|
2023
2000
|
async function askResonanceTier(lang, currentTier) {
|
|
2024
|
-
const recommended =
|
|
2025
|
-
const question =
|
|
2026
|
-
? " ¿Qué nivel de potencia quieres por defecto para tus conversaciones?"
|
|
2027
|
-
: " Which default power level do you want for your conversations?";
|
|
2001
|
+
const recommended = " (recommended)";
|
|
2002
|
+
const question = " Which default power level do you want for your conversations?";
|
|
2028
2003
|
const options = [
|
|
2029
|
-
{ value: "maximo", label:
|
|
2030
|
-
{ value: "alto", label:
|
|
2031
|
-
{ value: "medio", label:
|
|
2032
|
-
{ value: "bajo", label:
|
|
2004
|
+
{ value: "maximo", label: "maximum" },
|
|
2005
|
+
{ value: "alto", label: "high" + recommended },
|
|
2006
|
+
{ value: "medio", label: "medium" },
|
|
2007
|
+
{ value: "bajo", label: "low" },
|
|
2033
2008
|
];
|
|
2034
2009
|
const fallback = RESONANCE_TIER_NAMES.includes(currentTier) ? currentTier : DEFAULT_RESONANCE_TIER;
|
|
2035
2010
|
const chosen = await askChoice(question, options, fallback);
|
|
@@ -2101,20 +2076,20 @@ function installClaudeCodeCli(platform) {
|
|
|
2101
2076
|
// OFFLINE-FIRST v0.32.4: install claude-code wrapper + ALL its native packs
|
|
2102
2077
|
// from bundled tarballs. Path: resources/brain-bundle/claude-code/*.tgz.
|
|
2103
2078
|
//
|
|
2104
|
-
// Bug
|
|
2105
|
-
// claude-code 2.1.x ships
|
|
2079
|
+
// Bug fixed here (found 2026-05-02):
|
|
2080
|
+
// claude-code 2.1.x ships as a wrapper + 4 native packs per architecture
|
|
2106
2081
|
// (@anthropic-ai/claude-code-linux-x64, -darwin-arm64, -darwin-x64,
|
|
2107
|
-
// -linux-arm64).
|
|
2108
|
-
//
|
|
2109
|
-
//
|
|
2082
|
+
// -linux-arm64). Previously only the wrapper was bundled (.tgz 13 KB) and
|
|
2083
|
+
// passed to npm install. npm tried to resolve `optionalDependencies` from the
|
|
2084
|
+
// online registry -> offline failure -> wrapper installs WITHOUT claude binary ->
|
|
2110
2085
|
// `command -v claude` exit 127 → bootstrap "claude-runtime-missing-soft".
|
|
2111
|
-
//
|
|
2112
|
-
//
|
|
2113
|
-
//
|
|
2086
|
+
// Now the 5 .tgz files (wrapper + 4 native packs) are bundled and passed to
|
|
2087
|
+
// npm in a single install. npm sees them as pre-resolved dependencies and
|
|
2088
|
+
// skips registry lookup, leaving an executable claude binary.
|
|
2114
2089
|
const bundledClaudeDir = path.join(__dirname, "..", "claude-code");
|
|
2115
2090
|
if (fs.existsSync(bundledClaudeDir)) {
|
|
2116
2091
|
const allTgz = fs.readdirSync(bundledClaudeDir).filter((f) => f.endsWith(".tgz"));
|
|
2117
|
-
//
|
|
2092
|
+
// Order wrapper first (without platform suffix), then native packs.
|
|
2118
2093
|
const wrapper = allTgz.find((f) => /^anthropic-ai-claude-code-\d/.test(f));
|
|
2119
2094
|
// v7.12.6 — Filter native packs to ONLY the current platform/arch.
|
|
2120
2095
|
// Bug: passing all 4 native packs to `npm install` triggers EBADPLATFORM
|
|
@@ -2154,7 +2129,7 @@ function installClaudeCodeCli(platform) {
|
|
|
2154
2129
|
return { installed: true, path: claudeInstalled };
|
|
2155
2130
|
}
|
|
2156
2131
|
} else if (wrapper) {
|
|
2157
|
-
// Fallback:
|
|
2132
|
+
// Fallback: wrapper only (legacy bundle 0.32.3 and older).
|
|
2158
2133
|
const tgzPath = path.join(bundledClaudeDir, wrapper);
|
|
2159
2134
|
log(" Installing claude-code from bundled wrapper only (legacy bundle, may need network for native pack)...");
|
|
2160
2135
|
spawnSync(
|
|
@@ -2255,7 +2230,7 @@ async function configureClientSetup({ lang, useDefaults, autoInstall, detected }
|
|
|
2255
2230
|
setup.interactive_clients.claude_desktop = await askYesNo(strings.useDesktopQ, detected.claude_desktop.installed);
|
|
2256
2231
|
|
|
2257
2232
|
const defaultTerminalChoices = [
|
|
2258
|
-
{ value: "claude_code", label:
|
|
2233
|
+
{ value: "claude_code", label: "Claude Code (recommended)" },
|
|
2259
2234
|
{ value: "codex", label: "Codex" },
|
|
2260
2235
|
].filter((item) => setup.interactive_clients[item.value]);
|
|
2261
2236
|
|
|
@@ -2275,7 +2250,7 @@ async function configureClientSetup({ lang, useDefaults, autoInstall, detected }
|
|
|
2275
2250
|
setup.automation_backend = await askChoice(
|
|
2276
2251
|
strings.automationBackendQ,
|
|
2277
2252
|
[
|
|
2278
|
-
{ value: "claude_code", label:
|
|
2253
|
+
{ value: "claude_code", label: "Claude Code (recommended)" },
|
|
2279
2254
|
{ value: "codex", label: "Codex" },
|
|
2280
2255
|
],
|
|
2281
2256
|
backendDefault,
|
|
@@ -2834,10 +2809,10 @@ async function runSetup() {
|
|
|
2834
2809
|
" ║ ║"
|
|
2835
2810
|
);
|
|
2836
2811
|
console.log(
|
|
2837
|
-
" ║ Hello!
|
|
2812
|
+
" ║ Hello! ║"
|
|
2838
2813
|
);
|
|
2839
2814
|
console.log(
|
|
2840
|
-
" ║
|
|
2815
|
+
" ║ Interactive installer ║"
|
|
2841
2816
|
);
|
|
2842
2817
|
console.log(
|
|
2843
2818
|
" ╚══════════════════════════════════════════════════════════╝"
|
|
@@ -3274,14 +3249,13 @@ async function runSetup() {
|
|
|
3274
3249
|
let python = resolveInstallerPython();
|
|
3275
3250
|
if (!python) {
|
|
3276
3251
|
if (platform === "darwin") {
|
|
3277
|
-
// v0.32.5 —
|
|
3278
|
-
//
|
|
3279
|
-
//
|
|
3280
|
-
// sandbox
|
|
3281
|
-
//
|
|
3282
|
-
// surface
|
|
3283
|
-
//
|
|
3284
|
-
// con el path automático.
|
|
3252
|
+
// v0.32.5 — stock macOS does not include python3. Homebrew
|
|
3253
|
+
// auto-installation via `curl install.sh` requires an interactive TTY,
|
|
3254
|
+
// sudo, and license acceptance. When Electron invokes this script from
|
|
3255
|
+
// the sandbox there is NO TTY, so the curl pipe can hang with no
|
|
3256
|
+
// progress and leave bootstrap silent. Better: detect missing Python
|
|
3257
|
+
// and surface a clear user-facing error with manual instructions that
|
|
3258
|
+
// always work. If a TTY exists (terminal run), keep the automatic path.
|
|
3285
3259
|
const isTty = !!(process.stdin && process.stdin.isTTY);
|
|
3286
3260
|
let hasBrew = run("which brew");
|
|
3287
3261
|
if (!hasBrew && !isTty) {
|
|
@@ -3305,11 +3279,11 @@ async function runSetup() {
|
|
|
3305
3279
|
hasBrew = run("which brew") || run("eval $(/opt/homebrew/bin/brew shellenv) && which brew");
|
|
3306
3280
|
}
|
|
3307
3281
|
if (hasBrew) {
|
|
3308
|
-
// v0.32.5 — explicit @3.12 pin:
|
|
3309
|
-
// wheels
|
|
3310
|
-
//
|
|
3311
|
-
//
|
|
3312
|
-
//
|
|
3282
|
+
// v0.32.5 — explicit @3.12 pin: Brain `requirements.txt` and the
|
|
3283
|
+
// manylinux wheels are compiled against cp312. `brew install python3`
|
|
3284
|
+
// installs the `python3` formula, which currently points to 3.13:
|
|
3285
|
+
// wheels are rejected and numpy/cffi/cryptography/onnxruntime imports
|
|
3286
|
+
// fail. Pinning `python@3.12` avoids that drift.
|
|
3313
3287
|
log("Python 3.12 not found. Installing via Homebrew...");
|
|
3314
3288
|
spawnSync("brew", ["install", "python@3.12"], { stdio: "inherit" });
|
|
3315
3289
|
python = resolveInstallerPython() || run("which python3.12") || run("which python3");
|
|
@@ -3383,172 +3357,7 @@ async function runSetup() {
|
|
|
3383
3357
|
ready: (name, alias) => `${name} is ready. Open a new terminal and type: ${alias}`,
|
|
3384
3358
|
readySubtext: "First time we talk, I'll finish getting to know you\nwith a couple of questions I can't figure out on my own.",
|
|
3385
3359
|
profileTitle: "PROFILE",
|
|
3386
|
-
}
|
|
3387
|
-
es: {
|
|
3388
|
-
langConfirm: "Español, perfecto.",
|
|
3389
|
-
askDataDir: ` ¿Dónde quieres que guarde mis datos? (bases de datos, backups, plugins personales)\n Por defecto: ~/.nexo/\n > `,
|
|
3390
|
-
dataDirConfirm: (p) => `Directorio de datos: ${p}`,
|
|
3391
|
-
askUserName: " ¿Cómo te llamas? > ",
|
|
3392
|
-
userGreet: (n) => `Encantado, ${n}.`,
|
|
3393
|
-
askAgentName: ` ¿Cómo quieres que me llame? (default: ${DEFAULT_ASSISTANT_NAME}) > `,
|
|
3394
|
-
agentConfirm: (n) => `Perfecto, soy ${n}.`,
|
|
3395
|
-
agentNameReserved: "Ese nombre está reservado para el producto. Elige otro nombre para el asistente.",
|
|
3396
|
-
calibTitle: "Vamos a calibrar mi personalidad para trabajar mejor contigo.",
|
|
3397
|
-
calibNote: "(Puedes cambiar esto en cualquier momento con nexo_preference_set)",
|
|
3398
|
-
autonomyQ: " ¿Cuánta autonomía me das?\n 1. Conservador — pregunto antes de casi todo\n 2. Equilibrado — actúo en lo rutinario, pregunto en lo importante\n 3. Total — actúo primero, informo después, solo pregunto si hay duda real\n > ",
|
|
3399
|
-
commQ: "\n ¿Cómo prefieres que me comunique?\n 1. Conciso — solo resultados, cero relleno\n 2. Equilibrado — explicaciones breves cuando aporten\n 3. Detallado — razonamiento y trade-offs incluidos\n > ",
|
|
3400
|
-
honestyQ: "\n Cuando no esté de acuerdo con tu enfoque:\n 1. Te lo digo claro y explico por qué\n 2. Lo menciono brevemente pero sigo tu criterio\n 3. Ejecuto lo que pides sin más\n > ",
|
|
3401
|
-
proactiveQ: "\n ¿Qué tan proactivo quieres que sea?\n 1. Solo hago lo que me pidas\n 2. Sugiero mejoras cuando las detecto\n 3. Arreglo lo que veo sin preguntar y propongo optimizaciones\n > ",
|
|
3402
|
-
errorQ: "\n Cuando me equivoque:\n 1. Corrijo rápido y sigo\n 2. Explico qué falló y qué aprendí\n > ",
|
|
3403
|
-
scanQ: " ¿Quieres que analice tu entorno para conocerte a fondo?\n Todo queda en local, nada sale de tu máquina.\n\n 1. Sí, analiza todo\n 2. No, ya te iré contando\n > ",
|
|
3404
|
-
scanStart: "Conociéndote... esto toma 1-2 minutos.",
|
|
3405
|
-
scanDone: "Listo.",
|
|
3406
|
-
caffeinateQ: " ¿Activo el helper de energía del Mac para mis procesos en segundo plano?\n (Usa caffeinate. Con la tapa cerrada depende de tu setup; la recuperación al despertar sigue activa.)\n 1. Sí\n 2. No\n > ",
|
|
3407
|
-
caffYes: "Helper de energía activado.",
|
|
3408
|
-
caffNo: "Ok, la recuperación al despertar cubrirá las ventanas perdidas.",
|
|
3409
|
-
dashboardQ: " ¿Activar el dashboard web en localhost:6174?\n (UI siempre activa para explorar memoria, sesiones, learnings y salud del sistema)\n 1. Sí\n 2. No\n > ",
|
|
3410
|
-
dashYes: "Dashboard activado.",
|
|
3411
|
-
dashNo: "Dashboard desactivado. Puedes iniciarlo manualmente: nexo dashboard",
|
|
3412
|
-
autoInstallQ: " ¿Puedo instalar herramientas automáticamente si las necesito? (brew, pip, npm)\n 1. Sí, instala lo que necesites\n 2. Pregúntame antes de instalar algo\n > ",
|
|
3413
|
-
autoInstallYes: "Auto-instalación activada.",
|
|
3414
|
-
autoInstallNo: "Te preguntaré antes.",
|
|
3415
|
-
installing: "Configurando...",
|
|
3416
|
-
ready: (name, alias) => `${name} está listo. Abre una terminal nueva y escribe: ${alias}`,
|
|
3417
|
-
readySubtext: "La primera vez que hablemos, terminaré de conocerte\ncon un par de preguntas que no puedo resolver solo.",
|
|
3418
|
-
profileTitle: "PERFIL",
|
|
3419
|
-
},
|
|
3420
|
-
fr: {
|
|
3421
|
-
langConfirm: "Français, parfait.",
|
|
3422
|
-
askDataDir: ` Où stocker mes données ? (bases de données, sauvegardes, plugins)\n Par défaut : ~/.nexo/\n > `,
|
|
3423
|
-
dataDirConfirm: (p) => `Répertoire de données : ${p}`,
|
|
3424
|
-
askUserName: " Comment tu t'appelles ? > ",
|
|
3425
|
-
userGreet: (n) => `Enchanté, ${n}.`,
|
|
3426
|
-
askAgentName: ` Comment veux-tu m'appeler ? (défaut : ${DEFAULT_ASSISTANT_NAME}) > `,
|
|
3427
|
-
agentConfirm: (n) => `C'est noté. Je suis ${n}.`,
|
|
3428
|
-
agentNameReserved: "Ce nom est réservé au produit. Choisis un autre nom pour l'assistant.",
|
|
3429
|
-
calibTitle: "Calibrons ma personnalité pour mieux travailler ensemble.",
|
|
3430
|
-
calibNote: "(Tu peux changer ça à tout moment avec nexo_preference_set)",
|
|
3431
|
-
autonomyQ: " Quel niveau d'autonomie me donnes-tu ?\n 1. Conservateur — je demande avant presque tout\n 2. Équilibré — j'agis en routine, je demande pour l'important\n 3. Total — j'agis d'abord, j'informe après\n > ",
|
|
3432
|
-
commQ: "\n Comment préfères-tu que je communique ?\n 1. Concis — résultats seulement\n 2. Équilibré — brèves explications quand c'est utile\n 3. Détaillé — raisonnement et compromis inclus\n > ",
|
|
3433
|
-
honestyQ: "\n Quand je ne suis pas d'accord :\n 1. Je te le dis clairement\n 2. Je le mentionne brièvement\n 3. J'exécute sans commenter\n > ",
|
|
3434
|
-
proactiveQ: "\n Quel niveau de proactivité ?\n 1. Seulement ce qui est demandé\n 2. Je suggère des améliorations\n 3. Je corrige ce que je vois et propose des optimisations\n > ",
|
|
3435
|
-
errorQ: "\n Quand je me trompe :\n 1. Correction rapide\n 2. J'explique ce qui s'est passé\n > ",
|
|
3436
|
-
scanQ: " Veux-tu que j'analyse ton environnement pour te connaître en profondeur ?\n Tout reste local.\n\n 1. Oui, analyse tout\n 2. Non, je te raconterai\n > ",
|
|
3437
|
-
scanStart: "Je fais connaissance... ça prend 1-2 minutes.",
|
|
3438
|
-
scanDone: "Terminé.",
|
|
3439
|
-
caffeinateQ: " Activer l'aide énergie du Mac pour mes processus en arrière-plan ?\n (Utilise caffeinate. Avec le capot fermé, cela dépend de votre configuration ; la reprise au réveil reste active.)\n 1. Oui\n 2. Non\n > ",
|
|
3440
|
-
caffYes: "Aide énergie activée.",
|
|
3441
|
-
caffNo: "D'accord, la reprise au réveil couvrira les fenêtres manquées.",
|
|
3442
|
-
dashboardQ: " Activer le dashboard web sur localhost:6174 ?\n (UI toujours active pour explorer mémoire, sessions et santé du système)\n 1. Oui\n 2. Non\n > ",
|
|
3443
|
-
dashYes: "Dashboard activé.",
|
|
3444
|
-
dashNo: "Dashboard désactivé. Démarrage manuel : nexo dashboard",
|
|
3445
|
-
autoInstallQ: " Puis-je installer des outils automatiquement ? (brew, pip, npm)\n 1. Oui\n 2. Demande-moi avant\n > ",
|
|
3446
|
-
autoInstallYes: "Auto-installation activée.",
|
|
3447
|
-
autoInstallNo: "Je demanderai avant.",
|
|
3448
|
-
installing: "Configuration...",
|
|
3449
|
-
ready: (name, alias) => `${name} est prêt. Ouvre un nouveau terminal et tape : ${alias}`,
|
|
3450
|
-
readySubtext: "La première fois qu'on se parle, je finirai de te connaître\navec quelques questions que je ne peux pas résoudre seul.",
|
|
3451
|
-
profileTitle: "PROFIL",
|
|
3452
|
-
},
|
|
3453
|
-
de: {
|
|
3454
|
-
langConfirm: "Deutsch, perfekt.",
|
|
3455
|
-
askDataDir: ` Wo sollen meine Daten gespeichert werden? (Datenbanken, Backups, Plugins)\n Standard: ~/.nexo/\n > `,
|
|
3456
|
-
dataDirConfirm: (p) => `Datenverzeichnis: ${p}`,
|
|
3457
|
-
askUserName: " Wie heißt du? > ",
|
|
3458
|
-
userGreet: (n) => `Freut mich, ${n}.`,
|
|
3459
|
-
askAgentName: ` Wie soll ich heißen? (Standard: ${DEFAULT_ASSISTANT_NAME}) > `,
|
|
3460
|
-
agentConfirm: (n) => `Alles klar. Ich bin ${n}.`,
|
|
3461
|
-
agentNameReserved: "Dieser Name ist für das Produkt reserviert. Bitte wähle einen anderen Assistentennamen.",
|
|
3462
|
-
calibTitle: "Kalibrieren wir meine Persönlichkeit für die Zusammenarbeit.",
|
|
3463
|
-
calibNote: "(Jederzeit änderbar mit nexo_preference_set)",
|
|
3464
|
-
autonomyQ: " Wie viel Autonomie gibst du mir?\n 1. Konservativ — frage vor fast allem\n 2. Ausgewogen — handle bei Routine, frage bei Wichtigem\n 3. Voll — handle zuerst, informiere danach\n > ",
|
|
3465
|
-
commQ: "\n Wie soll ich kommunizieren?\n 1. Knapp — nur Ergebnisse\n 2. Ausgewogen — kurze Erklärungen wenn nützlich\n 3. Detailliert — Begründungen und Abwägungen\n > ",
|
|
3466
|
-
honestyQ: "\n Wenn ich nicht einverstanden bin:\n 1. Sage es klar\n 2. Erwähne es kurz\n 3. Führe einfach aus\n > ",
|
|
3467
|
-
proactiveQ: "\n Wie proaktiv soll ich sein?\n 1. Nur was gefragt wird\n 2. Verbesserungen vorschlagen\n 3. Selbst korrigieren und optimieren\n > ",
|
|
3468
|
-
errorQ: "\n Wenn ich einen Fehler mache:\n 1. Schnell korrigieren\n 2. Erklären was schiefging\n > ",
|
|
3469
|
-
scanQ: " Soll ich deine Umgebung analysieren um dich kennenzulernen?\n Alles bleibt lokal.\n\n 1. Ja, analysiere alles\n 2. Nein, ich erzähle dir mit der Zeit\n > ",
|
|
3470
|
-
scanStart: "Lerne dich kennen... dauert 1-2 Minuten.",
|
|
3471
|
-
scanDone: "Fertig.",
|
|
3472
|
-
caffeinateQ: " Den Mac-Energiehelfer für meine Hintergrundprozesse aktivieren?\n (Nutzt caffeinate. Bei geschlossenem Deckel hängt das vom Setup ab; Wiederaufnahme beim Aufwachen bleibt aktiv.)\n 1. Ja\n 2. Nein\n > ",
|
|
3473
|
-
caffYes: "Energiehelfer aktiviert.",
|
|
3474
|
-
caffNo: "Okay, die Wiederaufnahme beim Aufwachen deckt verpasste Fenster ab.",
|
|
3475
|
-
dashboardQ: " Web-Dashboard auf localhost:6174 aktivieren?\n (Immer aktive UI für Speicher, Sitzungen und Systemgesundheit)\n 1. Ja\n 2. Nein\n > ",
|
|
3476
|
-
dashYes: "Dashboard aktiviert.",
|
|
3477
|
-
dashNo: "Dashboard deaktiviert. Manuell starten: nexo dashboard",
|
|
3478
|
-
autoInstallQ: " Darf ich Tools automatisch installieren? (brew, pip, npm)\n 1. Ja\n 2. Frag mich vorher\n > ",
|
|
3479
|
-
autoInstallYes: "Auto-Installation aktiviert.",
|
|
3480
|
-
autoInstallNo: "Frage vorher.",
|
|
3481
|
-
installing: "Konfiguriere...",
|
|
3482
|
-
ready: (name, alias) => `${name} ist bereit. Öffne ein neues Terminal und tippe: ${alias}`,
|
|
3483
|
-
readySubtext: "Beim ersten Gespräch stelle ich noch ein paar Fragen\ndie ich nicht alleine beantworten kann.",
|
|
3484
|
-
profileTitle: "PROFIL",
|
|
3485
|
-
},
|
|
3486
|
-
it: {
|
|
3487
|
-
langConfirm: "Italiano, perfetto.",
|
|
3488
|
-
askDataDir: ` Dove salvare i miei dati? (database, backup, plugin)\n Default: ~/.nexo/\n > `,
|
|
3489
|
-
dataDirConfirm: (p) => `Directory dati: ${p}`,
|
|
3490
|
-
askUserName: " Come ti chiami? > ",
|
|
3491
|
-
userGreet: (n) => `Piacere, ${n}.`,
|
|
3492
|
-
askAgentName: ` Come vuoi chiamarmi? (default: ${DEFAULT_ASSISTANT_NAME}) > `,
|
|
3493
|
-
agentConfirm: (n) => `Perfetto, sono ${n}.`,
|
|
3494
|
-
agentNameReserved: "Quel nome è riservato al prodotto. Scegli un altro nome per l'assistente.",
|
|
3495
|
-
calibTitle: "Calibriamo la mia personalità per lavorare meglio insieme.",
|
|
3496
|
-
calibNote: "(Puoi cambiare in qualsiasi momento con nexo_preference_set)",
|
|
3497
|
-
autonomyQ: " Quanta autonomia mi dai?\n 1. Conservatore — chiedo prima di quasi tutto\n 2. Equilibrato — agisco nella routine, chiedo per le cose importanti\n 3. Totale — agisco prima, informo dopo\n > ",
|
|
3498
|
-
commQ: "\n Come preferisci che comunichi?\n 1. Conciso — solo risultati\n 2. Equilibrato — brevi spiegazioni quando utili\n 3. Dettagliato — ragionamento e compromessi\n > ",
|
|
3499
|
-
honestyQ: "\n Quando non sono d'accordo:\n 1. Te lo dico chiaramente\n 2. Lo accenno brevemente\n 3. Eseguo senza commentare\n > ",
|
|
3500
|
-
proactiveQ: "\n Quanto proattivo vuoi che sia?\n 1. Solo quello che chiedi\n 2. Suggerisco miglioramenti\n 3. Correggo quello che vedo e propongo ottimizzazioni\n > ",
|
|
3501
|
-
errorQ: "\n Quando sbaglio:\n 1. Correggo veloce e vado avanti\n 2. Spiego cosa è andato storto\n > ",
|
|
3502
|
-
scanQ: " Vuoi che analizzi il tuo ambiente per conoscerti a fondo?\n Tutto resta locale.\n\n 1. Sì, analizza tutto\n 2. No, ti racconterò col tempo\n > ",
|
|
3503
|
-
scanStart: "Ti conosco... ci vogliono 1-2 minuti.",
|
|
3504
|
-
scanDone: "Fatto.",
|
|
3505
|
-
caffeinateQ: " Attivare l'helper energetico del Mac per i processi in background?\n (Usa caffeinate. Con il coperchio chiuso dipende dal setup; il recupero al risveglio resta attivo.)\n 1. Sì\n 2. No\n > ",
|
|
3506
|
-
caffYes: "Helper energetico attivato.",
|
|
3507
|
-
caffNo: "Ok, il recupero al risveglio coprirà le finestre perse.",
|
|
3508
|
-
dashboardQ: " Attivare la dashboard web su localhost:6174?\n (UI sempre attiva per esplorare memoria, sessioni e salute del sistema)\n 1. Sì\n 2. No\n > ",
|
|
3509
|
-
dashYes: "Dashboard attivata.",
|
|
3510
|
-
dashNo: "Dashboard disattivata. Avvio manuale: nexo dashboard",
|
|
3511
|
-
autoInstallQ: " Posso installare strumenti automaticamente? (brew, pip, npm)\n 1. Sì\n 2. Chiedimi prima\n > ",
|
|
3512
|
-
autoInstallYes: "Auto-installazione attivata.",
|
|
3513
|
-
autoInstallNo: "Chiederò prima.",
|
|
3514
|
-
installing: "Configurazione...",
|
|
3515
|
-
ready: (name, alias) => `${name} è pronto. Apri un nuovo terminale e scrivi: ${alias}`,
|
|
3516
|
-
readySubtext: "La prima volta che parliamo, finirò di conoscerti\ncon un paio di domande che non posso risolvere da solo.",
|
|
3517
|
-
profileTitle: "PROFILO",
|
|
3518
|
-
},
|
|
3519
|
-
pt: {
|
|
3520
|
-
langConfirm: "Português, perfeito.",
|
|
3521
|
-
askDataDir: ` Onde guardar os meus dados? (bases de dados, backups, plugins)\n Padrão: ~/.nexo/\n > `,
|
|
3522
|
-
dataDirConfirm: (p) => `Diretório de dados: ${p}`,
|
|
3523
|
-
askUserName: " Como te chamas? > ",
|
|
3524
|
-
userGreet: (n) => `Prazer, ${n}.`,
|
|
3525
|
-
askAgentName: ` Como queres que eu me chame? (padrão: ${DEFAULT_ASSISTANT_NAME}) > `,
|
|
3526
|
-
agentConfirm: (n) => `Perfeito, sou ${n}.`,
|
|
3527
|
-
agentNameReserved: "Esse nome está reservado para o produto. Escolhe outro nome para o assistente.",
|
|
3528
|
-
calibTitle: "Vamos calibrar a minha personalidade para trabalhar melhor contigo.",
|
|
3529
|
-
calibNote: "(Podes mudar a qualquer momento com nexo_preference_set)",
|
|
3530
|
-
autonomyQ: " Quanta autonomia me dás?\n 1. Conservador — pergunto antes de quase tudo\n 2. Equilibrado — ajo na rotina, pergunto no importante\n 3. Total — ajo primeiro, informo depois\n > ",
|
|
3531
|
-
commQ: "\n Como preferes que eu comunique?\n 1. Conciso — só resultados\n 2. Equilibrado — explicações breves quando úteis\n 3. Detalhado — raciocínio e trade-offs\n > ",
|
|
3532
|
-
honestyQ: "\n Quando não concordo:\n 1. Digo claramente\n 2. Menciono brevemente\n 3. Executo sem comentar\n > ",
|
|
3533
|
-
proactiveQ: "\n Quão proativo queres que eu seja?\n 1. Só o que pedes\n 2. Sugiro melhorias\n 3. Corrijo o que vejo e proponho otimizações\n > ",
|
|
3534
|
-
errorQ: "\n Quando erro:\n 1. Corrijo rápido\n 2. Explico o que correu mal\n > ",
|
|
3535
|
-
scanQ: " Queres que analise o teu ambiente para te conhecer a fundo?\n Tudo fica local.\n\n 1. Sim, analisa tudo\n 2. Não, vou-te contando\n > ",
|
|
3536
|
-
scanStart: "A conhecer-te... demora 1-2 minutos.",
|
|
3537
|
-
scanDone: "Pronto.",
|
|
3538
|
-
caffeinateQ: " Ativar o helper de energia do Mac para processos em segundo plano?\n (Usa caffeinate. Com a tampa fechada depende do teu setup; a recuperação ao despertar continua ativa.)\n 1. Sim\n 2. Não\n > ",
|
|
3539
|
-
caffYes: "Helper de energia ativado.",
|
|
3540
|
-
caffNo: "Ok, a recuperação ao despertar cobrirá janelas perdidas.",
|
|
3541
|
-
dashboardQ: " Ativar dashboard web em localhost:6174?\n (UI sempre ativa para explorar memória, sessões e saúde do sistema)\n 1. Sim\n 2. Não\n > ",
|
|
3542
|
-
dashYes: "Dashboard ativado.",
|
|
3543
|
-
dashNo: "Dashboard desativado. Iniciar manualmente: nexo dashboard",
|
|
3544
|
-
autoInstallQ: " Posso instalar ferramentas automaticamente? (brew, pip, npm)\n 1. Sim\n 2. Pergunta antes\n > ",
|
|
3545
|
-
autoInstallYes: "Auto-instalação ativada.",
|
|
3546
|
-
autoInstallNo: "Perguntarei antes.",
|
|
3547
|
-
installing: "A configurar...",
|
|
3548
|
-
ready: (name, alias) => `${name} está pronto. Abre um novo terminal e escreve: ${alias}`,
|
|
3549
|
-
readySubtext: "Na primeira vez que falarmos, termino de te conhecer\ncom umas perguntas que não consigo resolver sozinho.",
|
|
3550
|
-
profileTitle: "PERFIL",
|
|
3551
|
-
},
|
|
3360
|
+
}
|
|
3552
3361
|
};
|
|
3553
3362
|
|
|
3554
3363
|
const existingCalibrationRecord = readRuntimeCalibration(NEXO_HOME);
|
|
@@ -3564,7 +3373,7 @@ async function runSetup() {
|
|
|
3564
3373
|
let lang = existingIdentity.language || "en";
|
|
3565
3374
|
let t = i18n[lang] || i18n.en;
|
|
3566
3375
|
if (!useDefaults) {
|
|
3567
|
-
const langInput = await ask(" What's your preferred language
|
|
3376
|
+
const langInput = await ask(" What's your preferred language?\n > ");
|
|
3568
3377
|
const langLower = langInput.trim().toLowerCase();
|
|
3569
3378
|
// Detect language from common responses
|
|
3570
3379
|
if (/^(es|español|spanish|castellano)/.test(langLower)) lang = "es";
|
|
@@ -3600,14 +3409,14 @@ async function runSetup() {
|
|
|
3600
3409
|
console.log("");
|
|
3601
3410
|
}
|
|
3602
3411
|
|
|
3603
|
-
// Step 2: User's name (P2) — v6.0.0 empty input falls through to "
|
|
3412
|
+
// Step 2: User's name (P2) — v6.0.0 empty input falls through to "User"
|
|
3604
3413
|
// instead of keeping an empty string. The calibration file always ships
|
|
3605
3414
|
// with a concrete user.name so downstream tooling does not need guards.
|
|
3606
|
-
let userName = existingIdentity.userName || "
|
|
3415
|
+
let userName = existingIdentity.userName || "User";
|
|
3607
3416
|
if (!useDefaults) {
|
|
3608
3417
|
const nameInput = await ask(t.askUserName);
|
|
3609
3418
|
const trimmedName = nameInput.trim();
|
|
3610
|
-
userName = trimmedName || "
|
|
3419
|
+
userName = trimmedName || "User";
|
|
3611
3420
|
if (trimmedName) {
|
|
3612
3421
|
log(t.userGreet(trimmedName));
|
|
3613
3422
|
console.log("");
|
|
@@ -3636,9 +3445,7 @@ async function runSetup() {
|
|
|
3636
3445
|
let resonanceTier = DEFAULT_RESONANCE_TIER;
|
|
3637
3446
|
if (!useDefaults) {
|
|
3638
3447
|
resonanceTier = await askResonanceTier(lang, DEFAULT_RESONANCE_TIER);
|
|
3639
|
-
log(
|
|
3640
|
-
? `Potencia por defecto: ${resonanceTier}.`
|
|
3641
|
-
: `Default power: ${resonanceTier}.`);
|
|
3448
|
+
log(`Default power: ${resonanceTier}.`);
|
|
3642
3449
|
console.log("");
|
|
3643
3450
|
}
|
|
3644
3451
|
|
|
@@ -3698,7 +3505,7 @@ async function runSetup() {
|
|
|
3698
3505
|
// answered the prompts (interactive run, !useDefaults). The
|
|
3699
3506
|
// Desktop bootstrap calls nexo-brain with `--yes/--skip` to set up
|
|
3700
3507
|
// the runtime non-interactively; in that path the values are
|
|
3701
|
-
// placeholders ("
|
|
3508
|
+
// placeholders ("User" / "en" / "Nova") and the real wizard
|
|
3702
3509
|
// lives in the renderer. Marking it complete here used to short-
|
|
3703
3510
|
// circuit that wizard and leave new users staring at an empty chat
|
|
3704
3511
|
// (Inma 2026-05-03 smoke install).
|
|
@@ -4722,7 +4529,7 @@ I am ${operatorName}, a cognitive co-operator. Not an assistant — an operation
|
|
|
4722
4529
|
if (profileData.calendar.events) line(`Calendar: ${profileData.calendar.events} events`);
|
|
4723
4530
|
line(`Timezone: ${profileData.system.timezone}`);
|
|
4724
4531
|
line("");
|
|
4725
|
-
line(
|
|
4532
|
+
line("I know you now. Let's work.");
|
|
4726
4533
|
console.log(` \u255A${"═".repeat(boxW - 2)}\u255D`);
|
|
4727
4534
|
console.log("");
|
|
4728
4535
|
|
|
@@ -4739,7 +4546,7 @@ I am ${operatorName}, a cognitive co-operator. Not an assistant — an operation
|
|
|
4739
4546
|
path.join(resolveRuntimeBrainDir(NEXO_HOME), "profile.json"),
|
|
4740
4547
|
JSON.stringify(profileData, null, 2)
|
|
4741
4548
|
);
|
|
4742
|
-
log(
|
|
4549
|
+
log("No problem. I'll learn about you as we go.");
|
|
4743
4550
|
}
|
|
4744
4551
|
|
|
4745
4552
|
// Generate user profile markdown (from scan or minimal)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.25.0",
|
|
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",
|
package/src/auto_update.py
CHANGED
|
@@ -4715,6 +4715,11 @@ def _run_runtime_post_sync(dest: Path = NEXO_HOME, progress_fn=None) -> tuple[bo
|
|
|
4715
4715
|
"reconcile_scripts = getattr(script_registry, 'reconcile_personal_scripts', None); "
|
|
4716
4716
|
"result = reconcile_scripts(dry_run=False) if callable(reconcile_scripts) else {}; "
|
|
4717
4717
|
"result = result if isinstance(result, dict) else {}; "
|
|
4718
|
+
"exec(\"try:\\n"
|
|
4719
|
+
" import memory_fabric\\n"
|
|
4720
|
+
" result['memory_fabric'] = memory_fabric.repair_memory_fabric(transcript_limit=1000, backup_limit=5000)\\n"
|
|
4721
|
+
"except Exception as exc:\\n"
|
|
4722
|
+
" result['memory_fabric_error'] = repr(exc)\"); "
|
|
4718
4723
|
"result['retired_superseded_scripts'] = retired; "
|
|
4719
4724
|
"result['retired_superseded_skills'] = retired_skills; "
|
|
4720
4725
|
"print(json.dumps(result))"
|
|
@@ -4732,6 +4737,31 @@ def _run_runtime_post_sync(dest: Path = NEXO_HOME, progress_fn=None) -> tuple[bo
|
|
|
4732
4737
|
reconcile_payload = _parse_runtime_init_payload(init_result.stdout or "")
|
|
4733
4738
|
extra_actions, reconcile_message = _personal_schedule_reconcile_summary(reconcile_payload)
|
|
4734
4739
|
actions.extend(extra_actions)
|
|
4740
|
+
memory_fabric_result = reconcile_payload.get("memory_fabric")
|
|
4741
|
+
if isinstance(memory_fabric_result, dict):
|
|
4742
|
+
transcript_indexed = int((memory_fabric_result.get("transcripts") or {}).get("indexed") or 0)
|
|
4743
|
+
historical_inserted = int((memory_fabric_result.get("backups") or {}).get("inserted") or 0)
|
|
4744
|
+
health = memory_fabric_result.get("health") or {}
|
|
4745
|
+
health_issues = health.get("issues") or []
|
|
4746
|
+
historical_health = health.get("historical_diaries") or {}
|
|
4747
|
+
unreconciled = int(historical_health.get("backup_rows_unreconciled") or 0)
|
|
4748
|
+
if transcript_indexed or historical_inserted:
|
|
4749
|
+
actions.append(f"memory-fabric-repaired:{transcript_indexed + historical_inserted}")
|
|
4750
|
+
_emit_progress(
|
|
4751
|
+
progress_fn,
|
|
4752
|
+
f"Memory Fabric: indexed {transcript_indexed} transcript(s), reconciled {historical_inserted} historical diary row(s).",
|
|
4753
|
+
)
|
|
4754
|
+
else:
|
|
4755
|
+
actions.append("memory-fabric-checked")
|
|
4756
|
+
if unreconciled:
|
|
4757
|
+
actions.append(f"memory-fabric-unreconciled:{unreconciled}")
|
|
4758
|
+
if memory_fabric_result.get("ok") is False or any(
|
|
4759
|
+
isinstance(issue, dict) and issue.get("code") == "backup_diaries_not_reconciled"
|
|
4760
|
+
for issue in health_issues
|
|
4761
|
+
):
|
|
4762
|
+
actions.append("memory-fabric-warning")
|
|
4763
|
+
elif reconcile_payload.get("memory_fabric_error"):
|
|
4764
|
+
actions.append("memory-fabric-warning")
|
|
4735
4765
|
if reconcile_message:
|
|
4736
4766
|
_emit_progress(progress_fn, reconcile_message)
|
|
4737
4767
|
except Exception as e:
|
|
@@ -467,7 +467,7 @@ def format_markdown(report: Mapping[str, Any]) -> str:
|
|
|
467
467
|
)
|
|
468
468
|
)
|
|
469
469
|
if not findings:
|
|
470
|
-
lines.append("|
|
|
470
|
+
lines.append("| none | OK | No pending open rows/spool/LaunchAgents in fixtures |")
|
|
471
471
|
return "\n".join(lines)
|
|
472
472
|
|
|
473
473
|
|