Commit Graph

20 Commits

Author SHA1 Message Date
Scott Idem
1fa5151d8a fix: correct V3 search filter key and response field names in ae_knowledge
- Filter key is "and" not "and_filters" (V3 API format)
- Entry IDs use journal_entry_id/id, not id_random (id_random is None)
- Dates use updated_on/created_on, not updated_at/created_at
- Total count lives in meta.data_list_count, not top-level total/count
- Inject query_string="%" when and filters present but no query, since
  the V3 search engine requires query_string for filters to apply
- Normalize tags from string to list in both entry_read and entries_list
- Fix order_by to use updated_on (not updated_at) in entries_list
- Correct ARCH__AE_INTEGRATION.md: and_filters → and, or_filters → or

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 21:12:44 -04:00
Scott Idem
71e472bebe feat: improved ae_journal_search + AE integration docs
Search improvements:
- Switched from LIKE on default_qry_str to query_string path (fulltext
  MATCH/AGAINST IN BOOLEAN MODE — uses the index, supports +/- boolean ops)
- Added tag filter (icontains on tags field)
- Added date_from / date_to filters (created_on gte/lte)
- Added type_code / topic_code exact-match filters
- Added sort_by / sort_order control (updated, created, name, priority)
- Added status / priority filters
- Added page parameter for pagination
- Richer output: updated date, tags, pagination hint
- Updated Gemini tool declaration with all new params

Docs:
- documentation/ARCH__AE_INTEGRATION.md — journal_entry full schema,
  search operator reference, current tool inventory, planned phases
  (broader AE integration: tasks, people, calendar, knowledge import)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 20:10:04 -04:00
Scott Idem
77327d97ad feat: improve AE Journal read toolset
- ae_journal_entry_read: expose full entry content by id_random (title,
  journal, tags, summary, full content with configurable truncation)
- ae_journal_entries_list: browse all entries in a journal newest-first,
  numbered with id/title/tags/summary/date and pagination support
- ae_journal_search: richer output — tags, updated date, 400-char preview
  (was 200), show summary OR preview (not both when summary exists)

_get_entry() was already implemented; read tool just exposes it properly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 19:47:59 -04:00
Scott Idem
36fdda6728 feat: add reminders_remove tool for single-reminder removal
- reminders_remove(index) removes one reminder by 1-based index
- reminders_list now returns numbered output (1. heading / body)
  so any model can easily identify which index to pass
- _parse_sections() / _sections_to_text() helpers for clean round-trip
- Not in CONFIRM_REQUIRED — targeted removal is safe without a gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 19:27:53 -04:00
Scott Idem
e0e3170de3 feat: regex support in email allowlist
Each entry in email_allowlist.json is treated as a re.fullmatch pattern
(case-insensitive). Allows domain wildcards, plus-addressing, and any
variation expressible as a regex. Invalid patterns are logged and skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 21:43:38 -04:00
Scott Idem
b8bc4ea21f feat: email_send allowlist — block sends to non-whitelisted addresses
Reads home/{username}/email_allowlist.json (JSON array of addresses).
Fails safe: if file is missing or address not listed, send is blocked with
an informative message. home/ is gitignored; create the file manually per user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 21:40:10 -04:00
Scott Idem
fd0fb76c08 feat: add email_send orchestrator tool
Wraps the existing email_utils.send_email helper as an admin-only tool.
Accepts to, subject, body (plain text); newlines converted to <br> for HTML part.
Registered in _CALLABLES, _ALL_DECLARATIONS, and TOOL_ROLES (admin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 21:35:29 -04:00
Scott Idem
334e7f0dea feat: role-based tool access, confirmation gates, and new orchestrator tools
- auth_utils: get_user_role() reads role from auth.json (admin|user, default user)
- manage_passwords: new `role` command to promote/demote users (admin-only by convention)
- tools/__init__: TOOL_ROLES map, CONFIRM_REQUIRED set, get_tools_for_role(),
  get_openai_tools_for_role() — both orchestrators now filter tools by caller's role
- tools/system: cortex_restart (detached subprocess, 5s delay), cortex_logs (admin-only)
- tools/web: http_fetch — direct URL fetch, distinct from web_search
- tools/files: file_list (directory listing), file_write (restricted paths, admin-only)
- tools/notify: nc_talk_send — proactive outbound via notification.py
- orchestrator_engine + openai_orchestrator: user_role param; CONFIRM_REQUIRED tools
  return a confirmation-request result instead of executing — loop breaks after Claude
  asks user to confirm in a follow-up message
- home/scott/auth.json: role set to admin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 19:23:53 -04:00
Scott Idem
ed191cf0b4 feat: add journal entry update, disable, append, prepend tools
Four new tools for full journal entry lifecycle management:

- ae_journal_entry_update  — PATCH any combination of fields (title,
  content, summary, tags, enable); only provided fields are changed
- ae_journal_entry_disable — soft-delete via enable=false
- ae_journal_entry_append  — fetch entry, append timestamped section
  to the bottom (ideal for running logs / data logs)
- ae_journal_entry_prepend — fetch entry, prepend timestamped section
  to the top (most-recent-first pattern)

Shared _get_entry / _patch_entry helpers keep the read-modify-write
logic DRY. Also fixed journal_entry_create to prefer the canonical
journal_entry_id field over the legacy id_random alias.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:02:22 -04:00
Scott Idem
44f215c764 feat: add ae_journal_list tool
Lists all Aether Journals for the configured account via
POST /v3/crud/journal/search with no filters (account scoped by header).
Returns name + id_random for each journal so the agent can discover
available journals before searching or writing entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 21:50:02 -04:00
Scott Idem
1cc7988953 feat: add shell_exec tool and fix orchestrator model name resolution
- Add shell_exec to orchestrator tool suite (system.py + __init__.py)
  Runs arbitrary shell commands on the Cortex host with timeout (1–120s),
  combined stdout/stderr output, optional working_dir, and exit code reporting.
  Enables system diagnostics (df, ls, ps, journalctl, etc.) from Agent mode.

- Fix orchestrator_engine.run() to use model_name from resolved registry entry
  Previously used settings.orchestrator_model (.env hardcode) regardless of
  what model was assigned to the orchestrator role. Now accepts model_name param
  and falls back to settings value only when registry has no model_name.

- Update ARCH__FUTURE.md: date, running host, local orchestrator status,
  model registry V2 progress, added Cortex Mesh concept (section 9)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 20:29:46 -04:00
Scott Idem
d9a322164a feat: OpenAI-compatible orchestrator + backend auto-routing
- openai_orchestrator.py — new ReAct tool loop engine for any
  OpenAI-compatible endpoint (OpenRouter, Open WebUI, Ollama, LiteLLM);
  model handles both tool loop and final response, no Claude handoff needed
- tools/__init__.py — auto-derive OpenAI JSON Schema from existing Gemini
  FunctionDeclarations so tool definitions have a single source of truth
- routers/orchestrator.py — route to openai_orchestrator when model registry
  "orchestrator" role resolves to a local_openai type host
- routers/chat.py — pass role to _backend_label(); fix fallback_used logic
  (only meaningful for explicit backend overrides, not auto-routing)
- static/app.js — add null/"auto" to backend cycle; fetch local model hint
  without overriding the auto default on page load
- model_registry.py — _normalize() back-fills host_type on old registry files
- requirements.txt — add openai>=1.0.0
- ARCH__BACKENDS.md — document OpenAI-compat backend and routing logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 19:18:18 -04:00
Scott Idem
e5b6d58889 feat: reminders_add and reminders_list tools
- New cortex/tools/reminders.py with reminders_add, reminders_list, reminders_clear
- reminders_clear moved here from cron.py (cron still imports from same file)
- __init__.py: wired up new callables and Gemini declarations
- Inara can now add/read reminders in Agent mode via the orchestrator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:14:22 -04:00
Scott Idem
77e770cdb2 feat: multi-user/multi-persona support with two-level home directory layout
Restructures persona storage from a flat personas/{name}/ layout to
home/{username}/persona/{name}/, mirroring Linux home directories.

Changes:
- persona.py: two ContextVars (user + persona), Linux-style name validation,
  set_context(), get_user(), get_persona(), validate(), list_users(),
  list_user_personas(); persona_path() takes (username, name)
- config.py: replaces personas_dir with home_dir + home_root()
- git mv personas/inara → home/scott/persona/inara (history preserved)
- home/holly/persona/tina/: Holly's persona stub added
- cron_runner.py: all storage functions take (username, persona) params
- tools/cron.py: stamps user + persona on jobs; APScheduler IDs are
  {user}:{persona}:{job_id} to prevent collisions across users
- memory_distiller.py: distill_short/mid/long take (username, persona);
  added missing Path + settings imports
- scheduler.py: _load_user_crons() iterates home/*/persona/* (two-level)
- routers/chat.py, orchestrator.py: user field added; set_context() called
- tests/conftest.py: home_root fixture with two-level structure;
  patches home_dir instead of personas_dir
- tests/test_persona.py: fully rewritten for two-level API
- tests/test_api_files.py: updated fixture name and path
- .env.default: documents HOME_DIR setting; scrubs stale API key
- CLAUDE.md, README.md: directory maps updated for new layout

All 80 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 22:35:40 -04:00
Scott Idem
5cadb836fa feat: multi-persona support (single Cortex, multiple users)
- Add cortex/persona.py: ContextVar-based per-request routing with
  path traversal protection and persona validation
- Migrate inara/ → personas/inara/ (git history preserved via git mv)
- config.py: add personas_root(), inara_path() delegates to personas/inara
- All 14 settings.inara_path() call sites replaced with persona_path()
- ChatRequest + OrchestrateRequest: add persona field (default: "inara")
  with validation at request entry before any processing
- memory_distiller: add optional persona param for future per-persona distill
- cron_runner/tools/cron: stamp persona on jobs, prefix APScheduler IDs
  (persona:job_id) to prevent collisions across personas
- scheduler: _load_user_crons() iterates all personas at startup

Adding a new persona: create personas/<name>/ with IDENTITY.md + SOUL.md.
Auth: handled at nginx level (inject X-Cortex-Persona header per subdomain).
Future: persona maps to Aether account_id_random for full integration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:50:02 -04:00
Scott Idem
6316ffa1d4 feat: cron job system for Inara (remind + note types)
- cron_runner.py: job storage (CRONS.json), schedule parsing, execution
- tools/cron.py: cron_list/add/remove/toggle + reminders_clear tools
- scheduler.py: load user crons at startup, expose get_scheduler() for
  live add/remove without restarts
- context_loader.py: auto-include REMINDERS.md in system prompt (tier 2+)
  so cron reminders surface automatically without Inara having to poll
- inara/CRONS.json + REMINDERS.md: backing files (initially empty)

Schedule formats: hourly | daily | daily:HH:MM | weekly:DOW | weekly:DOW:HH:MM
Job types: remind (→ REMINDERS.md) | note (→ SCRATCH.md)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:17:49 -04:00
Scott Idem
1b32667872 feat: scratchpad tool + fix Claude auth token expiry warning
- Add cortex/tools/scratch.py with scratch_read/write/append/clear tools
- Register all four scratch tools in the orchestrator tool registry
- Create inara/SCRATCH.md as the backing file (never distilled/archived)
- Fix auth.py: expiresAt reflects short-lived access token (~8h) not the
  1-year refresh token — suppress expiry warning when refreshToken is present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:10:03 -04:00
Scott Idem
24d16734a5 feat: personal task management tools for Inara
Adds task_list, task_create, task_update, task_complete orchestrator tools
backed by inara/TASKS.json — private to each agent instance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:17:38 -04:00
Scott Idem
9b818aa5c7 feat: orchestrator Agent mode UI + claude_allow_dir tool + fix DDG search
- Add Agent mode toggle to web UI input row — routes through POST /orchestrate
  instead of /chat; polls for result with live tool-call count in thinking bubble
- Add cortex/tools/system.py with claude_allow_dir tool; registers in tool registry
- Fix web search: duckduckgo_search renamed to ddgs, update import + requirements.txt
- Allow WebSearch and WebFetch in ~/.claude/settings.json for Claude CLI fallback
- Add claude-allow-dir script docs and security note to CLAUDE.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:42:44 -04:00
Scott Idem
ed472ce9a0 feat: Intelligence Layer Phase 1 — orchestrator service
Adds the Gemini API orchestrator (ReAct tool loop → Claude responder):

Orchestrator engine + router:
- orchestrator_engine.py: Gemini API tool loop, Claude CLI handoff
- routers/orchestrator.py: POST /orchestrate (async job queue), GET /orchestrate/{job_id}

Tools (cortex/tools/):
- web.py: DuckDuckGo web search (no key required)
- ae_knowledge.py: ae_journal_search + ae_journal_entry_create (AE V3 API)
- ae_tasks.py: ae_task_list (reads agents_sync Kanban filesystem)
- files.py: file_read (path-allowlisted to safe dirs)

Config + deps:
- config.py: orchestrator, DuckDuckGo, and AE API settings
- requirements.txt: google-genai, duckduckgo-search
- .env.default: reference config with all new keys documented

Docs:
- CLAUDE.md, README.md, documentation/ added to repo
- Port references updated 7331 → 8000 throughout
- Default model updated to gemini-2.5-flash

Tested: ae_task_list, ae_journal_search, web_search all working end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 19:37:49 -04:00