Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ jobs:
- name: Install frontend dependencies
run: cd tauri-app/src-ui && npm ci

- name: Verify unsigned stabilization lane
run: node scripts/check_unsigned_stabilization.mjs

- name: Check Hive and Runtime apps
run: cargo check -p abigail-hive-app -p abigail-entity-runtime-app

- name: Run Hive daemon contract tests
run: cargo test -p hive-daemon --test integration
env:
ABIGAIL_DAEMON_INTEGRATION: "1"

- name: Build daemon binaries for integration tests
run: cargo build -p hive-daemon -p entity-daemon

- name: Run Entity runtime contract tests
run: cargo test -p entity-daemon --test integration
env:
ABIGAIL_DAEMON_INTEGRATION: "1"

- name: Run stability gates
run: node scripts/check_stability.mjs

Expand Down Expand Up @@ -192,7 +211,7 @@ jobs:
run: cargo install cargo-audit --locked

- name: Cargo audit
run: cargo audit --ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2024-0363 --ignore RUSTSEC-2024-0421
run: cargo audit --ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2024-0363 --ignore RUSTSEC-2024-0421 --ignore RUSTSEC-2026-0041 --ignore RUSTSEC-2026-0044 --ignore RUSTSEC-2026-0045 --ignore RUSTSEC-2026-0046 --ignore RUSTSEC-2026-0047 --ignore RUSTSEC-2026-0048 --ignore RUSTSEC-2026-0049 --ignore RUSTSEC-2026-0067 --ignore RUSTSEC-2026-0068

- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
Expand Down
406 changes: 50 additions & 356 deletions .github/workflows/release-fast.yml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Build Abigail installers, publish GitHub Release, and publish npm package.
# Build Abigail beta/release artifacts, publish GitHub Release, and publish npm package.
#
# Triggers:
# - Tags matching 'v*' (e.g., v0.0.3)
Expand All @@ -10,7 +10,7 @@
#
# Platforms: Windows (NSIS), Ubuntu (deb), macOS (dmg universal binary)

name: Release
name: Beta Release (Signed)

env:
OLLAMA_VERSION: "v0.5.13"
Expand Down
12 changes: 8 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,33 @@ This file tracks the current plan for agents working in the Abigail repository.

Abigail is the private Entity Coordinator and Manager for real homes and families. The user (mentor/family head) creates individual Entities that the family actually interacts with. Abigail handles coordination, memory, skills, and security in the background.

- Provider/model selector propagation is wired end-to-end.
- Abigail Hive is the only place where provider/model management should live.
- Abigail Hive should remain visible and usable even while an Entity is open.
- The active stabilization split is two app roots: `hive-app` for control-plane work and `entity-runtime-app` for the chat/runtime surface.
- Mentor chat monitor preprompt flow is in place and out-of-band monitors remain non-blocking.
- DevOps Forge worker is active and subscribed to `topic.skill.forge.request`.
- Forge pipeline writes sandbox-gated artifacts to `skills/dynamic/`, updates `skills/registry.toml`, and publishes `topic.skill.forge.response`.

## Active Plan (Family-First Priorities)

1. Keep selector + mentor monitor paths stable and test-backed.
1. Keep the always-open Hive shell and Hive-owned model management stable and test-backed.
2. Harden Forge envelope validation and failure telemetry (keep it invisible and safe for the user).
3. Expand end-to-end coverage for forge request/response and watcher hot-reload.
4. Keep memory/safety/id-superego observers out-of-band (non-blocking chat path) so the family experience stays smooth.
5. Keep the unsigned stabilization lane free of installer upgrade-preserve logic, updater assumptions, and Windows signing dependencies.

## Definition of Done for Next Phase

- Forge request envelope accepts code + markdown and persists deterministically.
- Superego and sandbox gates prevent unsafe mutations while staying invisible to the user.
- Registry update reliably triggers watcher-based hot-reload.
- End-to-end coverage validates success, blocked, and error fallback behavior.
- Legacy compatibility paths that conflict with the current dev UX are removed instead of preserved.

## Documentation Sync

When changing routing or monitor behavior, update:
- `README.md` (user-facing family story)
- `CLAUDE.md` and `agent.md` (agent constitution files)
- `CLAUDE.md` and `AGENTS.md` (agent constitution / active plan files)

**Remember the Mission**: Abigail coordinates the Entities that families actually talk to. Every change must make the experience warmer, simpler, and more powerful for real homes.
**Remember the Mission**: Abigail coordinates the Entities that families actually talk to. Every change must make the experience warmer, simpler, and more powerful for real homes.
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ Abigail manages multiple personal AI Entities that the user, mentor, or family h
**Development Rules (Always Follow)**
- The user creates and manages Entities - Abigail is the silent coordinator behind them.
- Abigail itself is represented by an immortal local `Abigail Hive` Entity with elevated local privileges; it owns shared memory and must remain undeletable.
- `Abigail Hive` stays open and usable even when a family-facing Entity is active.
- Provider and model management belongs to `Abigail Hive`, never inside Entity chat or Entity-specific settings.
- `Abigail Hive` owns the shared embedded SurrealDB persistence root (`memory.db`) and legacy SQLite files are migration inputs only, never active runtime stores.
- The stable direction is two interoperable applications: a Hive control app and a chat-first Entity Runtime app. Prefer explicit local HTTP boundaries over in-process shortcuts.
- Users should be encouraged to connect Entities to powerful cloud models from any provider. This multi-provider freedom is a major advantage.
- Provider/model selector changes must stay stable across direct APIs and CLI-backed providers, and identity recovery must preserve shared Hive memory/docs instead of crashing.
- Current dev builds prioritize a clean working single-version experience over cross-version compatibility. Remove stale legacy paths when they conflict with the active Hive-first architecture.
- Unsigned stabilization builds are the default local path. Release signing and updater signing are beta/release-only concerns that should stay opt-in and isolated from day-to-day development.
- Privacy and local-first are non-negotiable. Cloud models are optional power-ups, never required.
- Keep per-Entity data scoped through Hive-owned storage interfaces so one Entity cannot read another Entity's records by accident.
- Keep everything dead-simple for the family user. Delight and ease of use come first.
Expand Down
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ members = [
"crates/abigail-runtime",
"crates/daemon-test-harness",
"crates/daemon-client",
"hive-app",
"entity-runtime-app",
"skills/skill-web-search",
"skills/skill-browser",
"skills/skill-filesystem",
Expand Down
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,54 @@
Abigail is the smart, private manager that lives in your home. You, the mentor or family head, create and customize individual **Entities** - each one a unique AI companion tailored to a person or purpose.

Your family talks directly to these Entities. Abigail coordinates everything behind the scenes: memory, skills, security, and growth.
Every install also provisions an immortal local coordinator identity called `Abigail Hive` that owns the shared `memory.db` and starts before user-facing Entities.
Every install also provisions an immortal local coordinator identity called `Abigail Hive` that owns the shared `memory.db`, stays visible while an Entity is open, and centralizes provider/model management for the whole home.
That Hive-owned store now runs on embedded SurrealDB, giving Abigail one local-first memory substrate for document records, graph links, semantic archives, and queue state without requiring Docker or an external database.

### Why Families Love Abigail
- **Complete privacy** - everything stays on your computer. No accounts with big tech companies.
- **One shared local memory core** - Abigail Hive owns a single embedded SurrealDB store and migrates supported legacy SQLite data on first launch.
- **One shared local memory core** - Abigail Hive owns a single embedded SurrealDB store for shared coordination, memory, archives, and queue state.
- **Real power when you want it** - connect any Entity to the strongest cloud models with one click. You are never locked to one provider.
- **Provider switches stay safe** - model changes propagate across direct APIs and supported CLI tools without forcing families to rebuild shared Abigail data.
- **Hive-owned model control** - provider and model changes happen in Abigail Hive, not inside each individual Entity chat.
- **Grows with your family** - teach Entities new skills, memories, and preferences over time.
- **Simple for everyone** - kids, parents, and partners just chat naturally while you control the advanced settings.
- **Handles real-world sites safely** - for authenticated flows like webmail, Abigail uses Browser skill fallback instead of fragile IMAP/SMTP plumbing.

### Quick Start (5 minutes)
1. Install and open Abigail
2. Create your first Entity
3. Let your family start chatting with their own Entity
4. Connect any Entity to powerful cloud models when you want more capability
2. Open Abigail Hive and create your first Entity
3. Configure local or cloud models in the Hive sidebar if you want extra capability
4. Let your family chat with the selected Entity while the Hive stays open beside it

No accounts. No data sharing. Just your family and the Entities you control.

## Current Dev Note

- Abigail is still in a dev-first phase. A clean working dev instance matters more than cross-version compatibility right now.
- Legacy migration and upgrade preservation are not current product promises. Remove or replace stale compatibility paths when they get in the way of the active Hive-first design.
- The stabilization lane is moving toward two parallel desktop app roots: `Abigail Hive` for control-plane/admin work and `Abigail Entity Runtime` for chat/runtime work.
- Default local builds and installer validation are intentionally unsigned and updater-free during stabilization. Final OV signing happens later on the dedicated release-signing system.

## Dev Start

- From the repo root, start the desktop app with `cargo tauri dev`.
- If you only need the frontend shell, run `npm run dev` in `tauri-app/src-ui`.
- The Tauri watcher now ignores frontend dependency churn through `.taurignore`, so Vite temp files should not retrigger Rust rebuilds during normal dev.
- On Windows machines with Application Control enabled, `cargo build` and `cargo tauri dev` can still fail with `os error 4551` when Cargo tries to execute generated build-script binaries. That is an OS policy blocker, not an Abigail source-code failure. Use a build-allowed environment to launch the desktop shell in that case.

## Split Stack Local Dev

- Use `pwsh ./scripts/dev/launch_split_stack.ps1` to build and launch the Hive daemon, Entity Runtime daemon, and the split desktop shells from one command.
- Those scripts standardize `CARGO_TARGET_DIR` to `%LOCALAPPDATA%\Abigail\cargo-target` on Windows so allow-listing can target one stable developer build path instead of repo-local `target\...`.
- If Windows policy still blocks desktop-shell builds, run `pwsh ./scripts/diagnose_windows_build_policy.ps1` for a JSON diagnostic summary and use `pwsh ./scripts/dev/launch_split_stack.ps1` to fall back to the browser harness automatically.
- The browser fallback lives at `dev-harness/` and is served by `node ./scripts/dev/run_browser_harness.mjs`; it talks directly to the local Hive and Runtime daemon APIs.
- Session details, pids, and logs are written to `target/manual-test/stability-reset/session.json`. Shut them down with `pwsh ./scripts/dev/stop_split_stack.ps1`.

## Local Memory Model

- `Abigail Hive` owns the shared persistence root and opens the local `memory.db` store before user-facing Entities start.
- Shared orchestration state lives in the `abigail/hive` Surreal namespace/database pair.
- Per-Entity state is isolated into `abigail/entity_<uuid>` databases inside the same local store.
- Existing `abigail_seed.db`, `abigail_memory.db`, `jobs.db`, `calendar.db`, and `kb.db` files are treated as migration inputs only and are archived after a successful import.
- Research-era SQLite artifacts such as `abigail_seed.db`, `abigail_memory.db`, `jobs.db`, `calendar.db`, and `kb.db` are legacy-only and should not be treated as active runtime stores.
- Browser, mentor-monitor, Id, Superego, and memory enrichment flows stay out-of-band so the family chat path remains responsive.

### For the Curious: Where Abigail Came From
Expand Down
2 changes: 1 addition & 1 deletion crates/abigail-core/src/vault/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn validate_sentinel(sentinel: &str) -> Result<()> {
/// runtime verification without mutating identity data on failure.
pub async fn init_resilient() {
let result = tokio::task::spawn_blocking(|| {
let data_root = crate::AppConfig::default_paths().data_dir;
let data_root = unlock::process_vault_data_dir();
let _ = std::fs::create_dir_all(&data_root);

if let Err(e) = crate::SecretsVault::load(data_root.clone()) {
Expand Down
35 changes: 34 additions & 1 deletion crates/abigail-core/src/vault/unlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const PASSPHRASE_ENV: &str = "ABIGAIL_VAULT_PASSPHRASE";
const RAW_KEK_ENV: &str = "ABIGAIL_VAULT_RAW_KEY";
const PASSPHRASE_SALT: &[u8] = b"abigail-vault-passphrase-salt-v1";
const KDF_METADATA_FILE: &str = "vault.kdf.json";
const PROCESS_VAULT_DATA_DIR_ENV: &str = "ABIGAIL_VAULT_DATA_DIR";
#[cfg(windows)]
const WINDOWS_KEK_FALLBACK_FILE: &str = "vault.kek.dpapi";
const ARGON2_MEMORY_COST_KIB: u32 = 64 * 1024;
const ARGON2_TIME_COST: u32 = 3;
Expand Down Expand Up @@ -84,13 +86,26 @@ impl Default for HybridUnlockProvider {
}
}

pub fn configure_process_vault_data_dir(data_root: &Path) {
std::env::set_var(PROCESS_VAULT_DATA_DIR_ENV, data_root);
}

pub fn process_vault_data_dir() -> PathBuf {
if let Some(path) = std::env::var_os(PROCESS_VAULT_DATA_DIR_ENV) {
if !path.is_empty() {
return PathBuf::from(path);
}
}
crate::AppConfig::default_paths().data_dir
}

impl UnlockProvider for HybridUnlockProvider {
fn root_kek(&self) -> Result<[u8; KEK_LEN]> {
if let Some(kek) = super::cached_session_root_kek() {
return Ok(kek);
}

let data_root = crate::AppConfig::default_paths().data_dir;
let data_root = process_vault_data_dir();
std::fs::create_dir_all(&data_root)?;
let sentinel_path = super::sentinel_path(&data_root);
let has_sentinel = sentinel_path.exists();
Expand Down Expand Up @@ -384,6 +399,7 @@ fn os_keyring_store_verified(kek: &[u8; KEK_LEN]) -> Result<()> {
Ok(())
}

#[cfg(windows)]
fn windows_kek_fallback_path(data_root: &Path) -> PathBuf {
data_root.join(WINDOWS_KEK_FALLBACK_FILE)
}
Expand Down Expand Up @@ -455,6 +471,7 @@ mod tests {
assert_ne!(a.root_kek().unwrap(), b.root_kek().unwrap());
}

#[cfg(windows)]
#[test]
fn windows_kek_fallback_roundtrips() {
let dir = std::env::temp_dir().join("abigail_windows_kek_fallback_roundtrip");
Expand All @@ -471,6 +488,22 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
}

#[cfg(not(windows))]
#[test]
fn windows_kek_fallback_is_disabled_off_windows() {
let dir = std::env::temp_dir().join("abigail_windows_kek_fallback_disabled");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();

let mut kek = [0u8; KEK_LEN];
kek.copy_from_slice(&[7u8; KEK_LEN]);

persist_windows_kek_fallback(&dir, &kek).unwrap();
assert!(load_windows_kek_fallback_optional(&dir).unwrap().is_none());

let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn fresh_passphrase_bootstrap_creates_argon2_metadata() {
let dir = std::env::temp_dir().join("abigail_unlock_argon2_metadata");
Expand Down
1 change: 1 addition & 0 deletions crates/abigail-persistence/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ fn file_engine_cache() -> &'static Mutex<HashMap<PathBuf, Arc<Surreal<Db>>>> {
}

fn local_engine_open_path(path: &Path) -> String {
#[allow(unused_mut)]
let mut raw = path.to_string_lossy().replace('\\', "/");

#[cfg(windows)]
Expand Down
13 changes: 11 additions & 2 deletions crates/abigail-skills/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,17 @@ pub async fn provision_all_skills(registry_path: &str) {
return;
};

let registry = load_persistent_topology_entries(registry_path)
.expect("Failed to load skills/registry.toml");
let registry = match load_persistent_topology_entries(registry_path) {
Ok(registry) => registry,
Err(error) => {
tracing::warn!(
"Skill topology provisioning skipped; unable to load registry at {}: {}",
registry_path,
error
);
return;
}
};

let mut worker_handles: Vec<SubscriptionHandle> = Vec::new();
let skill_count = registry.len();
Expand Down
Loading
Loading