diff --git a/cortex/openai_orchestrator.py b/cortex/openai_orchestrator.py index 0dbdb1c..dcf8282 100644 --- a/cortex/openai_orchestrator.py +++ b/cortex/openai_orchestrator.py @@ -26,6 +26,7 @@ from openai import AsyncOpenAI from config import settings from orchestrator_engine import OrchestrateCheckpoint, OrchestratorResult from tools import OPENAI_TOOL_SCHEMAS, call_tool, get_openai_tools_for_role, get_tools_for_role, CONFIRM_REQUIRED +import tool_audit logger = logging.getLogger(__name__) @@ -73,6 +74,7 @@ async def run( effective_confirm = (CONFIRM_REQUIRED - set(_confirm_allow)) | set(_confirm_deny) client, model_name, active_tools = _build_client(model_cfg, user_role, tool_list) + tool_audit.set_context("openai", model_cfg.get("label") or model_name) sys_content = (system_prompt or "") + _TOOL_INSTRUCTION messages: list[dict] = [{"role": "system", "content": sys_content}] diff --git a/cortex/orchestrator_engine.py b/cortex/orchestrator_engine.py index bfde052..cb54dd1 100644 --- a/cortex/orchestrator_engine.py +++ b/cortex/orchestrator_engine.py @@ -27,6 +27,7 @@ from config import settings from llm_client import complete from tools import TOOL_DECLARATIONS, call_tool, get_tools_for_role, CONFIRM_REQUIRED import usage_tracker +import tool_audit from persona import _user logger = logging.getLogger(__name__) @@ -140,6 +141,7 @@ async def run( ) client = genai.Client(api_key=api_key) + tool_audit.set_context("gemini", model_name or settings.orchestrator_model) _confirm_allow = frozenset(confirm_allow or ()) _confirm_deny = frozenset(confirm_deny or ()) diff --git a/cortex/static/HELP.md b/cortex/static/HELP.md index e649e86..fe64f15 100644 --- a/cortex/static/HELP.md +++ b/cortex/static/HELP.md @@ -6,7 +6,7 @@ and are appended automatically by help.html when present. --> -*Last updated: 2026-04-30* +*Last updated: 2026-05-05* --- @@ -16,7 +16,7 @@ |---|---| | **Sessions** | Open the sessions panel — list, resume, or start sessions | | **N** (sliders icon) | Open the Context & Memory panel (N = current context tier) | -| **☰** | Settings menu — Files, Account, Sign Out | +| **☰** | Settings menu — Files, push notification toggle, Account, Sign Out | | **?** | Open this help panel | The **Context & Memory** panel (sliders icon with tier number) contains all configuration options: @@ -59,7 +59,7 @@ The orchestrator runs a multi-step tool loop: The ⚡ toggle is **independent of the Role selector** — you can use any role (chat, coder, research, etc.) with or without tools. The orchestrator model is configured in **Account → Model Registry → Role Assignments → Orchestrator**. By default this is Gemini API. -The full tool reference is in the **Tools** tab. 30 tools across web, files, shell, system, tasks, cron, reminders, scratchpad, notifications, and Aether Journals. +The full tool reference is in the **Tools** tab. 40 tools across web, files, shell, system, tasks, cron, reminders, scratchpad, notifications, and Aether Journals. Tools mode is best for tasks requiring research, multi-step reasoning, or side effects (e.g. "search for X", "add a task", "what's on my list?", "append this to my journal"). Regular chat is faster for conversational turns. @@ -222,6 +222,19 @@ The **Files** button opens an editor for your persona's identity and memory file Toggle **preview** / **edit** to switch between rendered markdown and raw text. **Ctrl+S** saves, **Esc** closes. +The **Audit Log** group at the bottom of the sidebar (collapsed by default) lists tool call logs by date (`YYYY-MM-DD.jsonl`). Click any date to view a read-only table of every orchestrator tool call: time, tool name, status, model, args, and result snippet. Status is colour-coded: green = ok, red = error, amber = denied. + +--- + +## Push Notifications + +Cortex can send browser push notifications — even when the tab is closed. + +- Open **☰ → Enable notifications** and accept the browser permission prompt. +- Once enabled, the button shows **Notifications on** (in accent colour). +- Click again to disable. Subscriptions are stored per-device. +- The orchestrator's `web_push` tool lets Inara send you a push proactively (e.g. when a long task completes). + --- ## Context & Memory ( ⚙ panel ) @@ -305,6 +318,13 @@ For direct access or scripting: | `GET` | `/orchestrate/{job_id}` | Poll job status and result | | `GET` | `/settings/models` | Model registry UI | | `POST` | `/api/models/role` | Set a role assignment (JSON body) | +| `GET` | `/api/push/vapid-key` | VAPID public key (for push subscription) | +| `POST` | `/api/push/subscribe` | Register a push subscription | +| `DELETE` | `/api/push/subscribe` | Remove a push subscription | +| `GET` | `/api/audit/files` | List available audit log dates (own data) | +| `GET` | `/api/audit/day?date=` | Tool call entries for a specific date (own data) | +| `GET` | `/api/audit/recent` | Recent tool calls across days (admin) | +| `GET` | `/api/audit/stats` | Tool call counts by tool/status/user (admin) | | `GET` | `/health` | Health check — returns `{"status": "ok"}` | Chat request body (`POST /chat`): diff --git a/cortex/static/app.js b/cortex/static/app.js index 996352f..f71ba82 100644 --- a/cortex/static/app.js +++ b/cortex/static/app.js @@ -1547,6 +1547,7 @@ Time Tool Status + Model Args Result `; @@ -1554,11 +1555,13 @@ const tbody = document.createElement('tbody'); for (const e of entries) { const time = (e.ts || '').slice(11, 19); // HH:MM:SS + const model = e.model || e.engine || ''; const tr = document.createElement('tr'); tr.innerHTML = ` ${time} ${e.tool || '?'} ${e.status || '?'} + ${model} ${_fmtArgs(e.args)} ${ (e.result_snippet || '').replace(/ None: + """Call at the start of each orchestrator run to tag subsequent tool calls.""" + _audit_engine.set(engine) + _audit_model.set(model) + def _truncate_args(args: dict) -> dict: out = {} @@ -63,6 +74,8 @@ async def record( entry = { "ts": datetime.now().isoformat(timespec="seconds"), "user": user, + "engine": _audit_engine.get(), + "model": _audit_model.get(), "tool": tool, "args": _truncate_args(args), "status": status, diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 0c7056e..f7ba4b1 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -39,8 +39,7 @@ New tools for `cortex/tools/` — higher-value additions that fill obvious gaps. - [x] **`file_write`** — overwrite/append to home_root paths, admin-only, confirm-required — 2026-04-29 - [x] **`nc_talk_send`** — outbound NC Talk message via notification.py, admin-only — 2026-04-29 - [x] **`email_send`** — SMTP via email_utils, per-user regex allowlist in `home/{user}/email_allowlist.json`, managed via Settings UI textarea + Files panel raw editor — 2026-04-29 -- [ ] **`web_push`** — send a browser push notification (requires push subscription stored - per-user; pairs well with the PWA service worker already in place) +- [x] **`web_push`** — VAPID push via pywebpush; subscriptions in `home/{user}/push_subscriptions.json`; "Enable notifications" toggle in ☰ menu; sw.js push+notificationclick handlers — 2026-05-05 ### [Channel] Proactive notifications Inara reaches out on her own initiative via NC Talk or Google Chat when a reminder @@ -121,6 +120,15 @@ so Scott can see who's spending what. - [ ] Expose via `/api/usage` endpoint; add a summary row to the Settings page - [ ] Optional: soft spending limit with a warning toast when exceeded +### [Security] Tool call audit log — 2026-05-05 +Every orchestrator tool invocation logged to `home/{user}/tool_audit/YYYY-MM-DD.jsonl`. +- [x] `tool_audit.py` — JSONL writer with asyncio locks; ContextVars for engine/model set by each orchestrator at run start +- [x] Hook in `call_tool()` — fire-and-forget `asyncio.create_task`; captures status ok/error/denied, 300-char result snippet, args (truncated at 500 chars) +- [x] `GET /api/audit/files` — lists available dates for current user (self-service) +- [x] `GET /api/audit/day?date=` — returns entries for one date (self-service) +- [x] `GET /api/audit/recent` + `/stats` — cross-user aggregation (admin only) +- [x] "Audit Log" group in Files panel sidebar (collapsed by default) — read-only table with time/tool/status/model/args/result columns; colour-coded status + ### [Intelligence] Dev agent pipeline See `ARCH__Intelligence_Layer.md`. Full design not yet started. - [ ] Specialist agent: frontend (SvelteKit) code changes