Commit graph

164 commits

Author SHA1 Message Date
Vadym Samoilenko
23dba2fa7b fix: read tool_call_chunks for Anthropic streaming tool args
Anthropic/LangChain streams tool arguments as JSON string deltas in
chunk.tool_call_chunks (not chunk.tool_calls which has empty args dict).
Collect and accumulate both sources, then merge by tool_call_id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:51:39 +01:00
Vadym Samoilenko
fed7a2eab7 fix: accumulate tool call args across streaming chunks
Anthropic Claude streaming sends tool call chunks where the first chunk
has the name but empty args dict, and subsequent chunks have args but
no name. The previous deduplication only kept the first entry with a
name, discarding all the args. Now we accumulate: merge dict args and
concatenate string args (for OpenAI) across all chunks per tool_call_id.

Fixes "No code provided" error when code_interpreter is called.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:47:10 +01:00
Vadym Samoilenko
e3e260ae84 fix: correct get_current_user import in code_interpreter_proxy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:40:11 +01:00
Vadym Samoilenko
4c6696ed2b feat: add file download proxy and normalize code interpreter file objects
- Add /api/v1/code-interpreter/files/{session_id}/{file_id} proxy endpoint
  so the browser can download files from the internal LibreCodeInterpreter
- Normalize file objects in CodeInterpreterTool to consistent {id, name, session_id}
  shape regardless of which field names LibreCodeInterpreter returns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:36:23 +01:00
Vadym Samoilenko
829808081a fix: sync_agent always fetches usage stats via own DB session
Previously sync_agent was called without db in all 3 agent endpoints,
so usage_stats was None and tokens/conversations/unique_users were never
sent to the collector (showing N/A in AgentHub). Now sync_agent opens
its own AsyncSession when no db is passed, avoiding the closed
request-scoped session issue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:33:24 +01:00
Vadym Samoilenko
80f41c097b feat: add MASTER_API_KEY to code-interpreter service for admin dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:31:20 +01:00
Vadym Samoilenko
d3d16f42a5 fix: switch back to /exec for file detection, handle keepalive whitespace in response
/exec/programmatic is for agentic tool-calling and returns files:[].
/exec uses the runner with savefig/open keyword detection and uploads
files to MinIO. Response is streaming with whitespace keepalive then
JSON body — parse with resp.text.strip().

Also include download URLs in tool display output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:19:56 +01:00
Vadym Samoilenko
e0b1db36a8 fix: use /exec/programmatic endpoint — /exec is streaming (chunked), programmatic returns JSON
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:16:23 +01:00
Vadym Samoilenko
16c6794126 fix: create /app/ssl mount point for code-interpreter sandbox wrapper
wrapper_cmd mounts tmpfs over /app/ssl to hide SSL certs from sandboxes,
but the directory didn't exist so mount failed silently, killing the entire
sh -c chain before REPL could start. tmpfs: in compose creates it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:13:43 +01:00
Vadym Samoilenko
19d3736ebe fix: use privileged mode for code-interpreter — nsjail needs proc mount
SYS_ADMIN + seccomp:unconfined was insufficient; nsjail needs to mount
/proc in new mount namespaces which requires full privileged mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:07:39 +01:00
Vadym Samoilenko
28fda10b75 fix: add seccomp:unconfined to code-interpreter — nsjail needs mount syscalls
Seccomp filter was blocking mount/unshare/pivot_root syscalls required by
nsjail to build sandbox mount tree, causing all REPL processes to fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:05:46 +01:00
Vadym Samoilenko
8f2e685c42 feat: code interpreter toggle button in chat input
- Terminal button in chat input bar toggles code interpreter per-message
- Backend accepts &code_interpreter=true query param to force-enable the tool
- Works for any agent, regardless of enable_code_interpreter setting
- File attach button now uses supportsFileUpload prop (works for any agent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:03:17 +01:00
Vadym Samoilenko
64b7aa06c8 fix: auto-inject code_interpreter instruction into system prompt when enabled
LLM wasn't calling the tool because it didn't know it had code execution
capability. Now appends an explicit instruction whenever enable_code_interpreter
is True on the agent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:00:08 +01:00
Vadym Samoilenko
c7ce0debbe fix: replace broken iframe with direct link for code interpreter dashboard
CSS doesn't load correctly through Apache reverse proxy due to absolute
static asset paths. Direct external link is more reliable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:46:56 +01:00
Vadym Samoilenko
3ed1fca354 fix: correct stale port 8100 reference in deploy.sh note
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:42:39 +01:00
Vadym Samoilenko
e56c3adaca fix: change code-interpreter port to 8877 (8100 was in use), fix auth loading state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:40:12 +01:00
Vadym Samoilenko
ec932ce54f feat: API key auth for agent execution + code interpreter admin dashboard
- Add ApiKey model, migration (028), CRUD endpoints (/admin/api-keys/)
- get_current_user_or_api_key dependency: JWT Bearer or X-API-Key header
- Agent execute endpoint now accepts API keys (super_admin only to create)
- ApiKeysTab UI: list, create (one-time key reveal), revoke, delete
- Wired ApiKeysTab into admin page (super_admin only)
- Add /admin/code-interpreter page with iframe to LibreCodeInterpreter dashboard
- Expose code-interpreter on 127.0.0.1:8100 for Apache proxy
- deploy.sh: note Apache proxy rule for /nexus-code-interpreter/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:22:27 +01:00
Vadym Samoilenko
f2b5dce63a feat: code interpreter, agent analytics/execute APIs, usage sync, RAG scoping fixes
**Phase 1 — Agent Usage Sync to AgentHub Collector**
- Add agent_usage service: per-agent stats (messages, tokens, conversations, unique users, first/last used)
- Collector sync now includes usage data in payload; sync_agent accepts optional db session
- Celery beat task runs every 6h to sync all active agents with fresh usage stats

**Phase 2 — LibreCodeInterpreter Integration**
- Add code-interpreter, redis, minio services to docker-compose.prod.yml
- CodeInterpreterTool (BaseTool): sandboxed execution via /exec, 13 languages, Python session persistence via conversation_id
- ToolContext extended with conversation_id and agent_slug
- enable_code_interpreter boolean on Agent model (migration 027), tool seeded in tool_definitions (migration 026)
- Code interpreter auto-injected into agent tools when enabled
- Frontend: CodeExecutionResult component with terminal-style stdout/stderr/files rendering

**Phase 3 — Agent API Endpoints**
- GET /api/v1/agents/{slug}/analytics — per-agent usage stats + daily time series
- POST /api/v1/agents/{slug}/execute — synchronous programmatic agent execution (non-SSE)
- Sub-routes registered before /{slug} to avoid FastAPI route conflict

**Phase 4 — Fix Department & Region RAG Scoping**
- Department filter now OR-includes global (null department) docs, matching region filter behaviour
- retriever.search_documents/retrieve_and_prepare/query accept department_ids/region_codes lists
- MatchAny used for multi-value Qdrant filters; chat.py passes full arrays from knowledge_scope
- Admin PATCH /users/{id} now validates region_code against the regions table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 20:13:27 +01:00
Vadym Samoilenko
0f6c9ededd fix: import all models in sync script to resolve all SQLAlchemy relationships
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:47:01 +01:00
Vadym Samoilenko
592c22ac61 fix: explicitly import User before Agent in sync script to resolve mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:45:14 +01:00
Vadym Samoilenko
d5d265a628 fix: import User model in __init__ and sync script to resolve relationships
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:44:32 +01:00
Vadym Samoilenko
19cc5b81d8 fix: use asyncpg driver in sync script
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:43:35 +01:00
Vadym Samoilenko
a0f718c114 feat: sync Nexus agents to AgentHub collector on create/update/status change
- New service: app/services/agent_collector.py
  - Maps Nexus Agent fields to collector API payload
  - Marks agents with tool="Oliver Nexus", tags=["nexus","oliver",...]
  - Skips private non-system agents
  - Fire-and-forget via asyncio.create_task, errors only logged

- Hooked into agents.py: create_agent, update_agent, update_agent_status
  each trigger sync_agent() after successful DB commit

- scripts/sync_agents_to_collector.py — one-shot bulk sync of all
  active agents for initial population of the collector

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:38:59 +01:00
Vadym Samoilenko
d9419a3ec8 fix: whats-new modal shows empty list and duplicate announcements fetch
- Pass announcements state from sidebar down to WhatsNewModal as props
  to eliminate duplicate useAnnouncements() instance and API call
- Snapshot unread items before calling markAllRead so they remain
  visible after the readIds state updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:16:04 +01:00
Vadym Samoilenko
0df92c025e fix: full page reload after OAuth to prevent spinner loop on home route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:11:09 +01:00
Vadym Samoilenko
04c390dfa9 fix: redirect to home page after login instead of /chat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:03:00 +01:00
Vadym Samoilenko
20aa27f2b4 Revert "fix: Graph consent flow — MSAL reserved scopes crash + missing callback page"
This reverts commit fb3fc2f53d.
2026-03-30 17:59:57 +01:00
Vadym Samoilenko
fb3fc2f53d fix: Graph consent flow — MSAL reserved scopes crash + missing callback page
- graph_consent.py: filter offline_access/openid/profile before passing to MSAL
  (MSAL reserves these and throws ValueError if supplied as user scopes)
- graph_consent.py: return 501 with clear message when ENTRA_CLIENT_SECRET not set
- graph_token_manager.py: include client_secret in refresh_token request (required
  for confidential client); strip openid/profile from refresh scope; ensure
  offline_access is present to receive a new refresh_token
- Add /auth/graph-callback page: handles Microsoft redirect after consent,
  POSTs code to backend, notifies opener window on success, then closes popup
- app-shell.tsx: exclude /auth/graph-callback from sidebar layout

Requires ENTRA_CLIENT_SECRET and a web platform on the Azure app registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:47:48 +01:00
Vadym Samoilenko
af4161d107 feat: home dashboard page with quick actions, announcements, and feature overview
- Replace root redirect with a full home page for authenticated users
- Greeting by time of day with user's display name
- Quick Start cards linking to RAG, Assistant, Agents, and User Guide
- What's New section pulling from announcements API with unread indicators
- Platform Features panel highlighting key capabilities
- Logo in sidebar now navigates to home page on click

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:38:34 +01:00
Vadym Samoilenko
c3a81bbf11 fix: create_agent tool not available to Personal Assistant
Root cause: CreateAgentTool/ListAgentsTool were registered in-memory at
startup but had no rows in tool_definitions table. chat.py resolves tools
via get_enabled_tools(mode, db) which queries tool_definitions — so the
tools were invisible to the assistant agent.

- Migration 025: INSERT create_agent + list_agents into tool_definitions
  with is_enabled=true, allowed_modes=["assistant"]
- types/index.ts: add tool_ids to AgentListItem (fixes TS build error
  in agents/page.tsx line 97)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:25:24 +01:00
Vadym Samoilenko
d48472e0e5 feat: /agents catalog page + sidebar cleanup
- New /agents page: beautiful card grid with icon, category badge,
  capability chips (RAG, M365 Tools, File Upload), description, Open Chat CTA
- Layout: System Assistants (pinned top) → Your Agents → Templates
- Search bar + category filter (All / General / HR / Finance / IT / Operations)
- Empty state with Create Agent CTA; Admin Panel shortcut for managers
- Sidebar: replace full agent list with system bots only + single
  "Agent Mode" button showing count of custom agents → /agents
- Remove AgentPickerModal from sidebar (now lives on /agents page)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:22:49 +01:00
Vadym Samoilenko
36ef5197ae fix: user-guide ESLint errors — unused imports, unescaped entities, missing keys
- Remove unused lucide imports: Users, Settings, Zap, Eye, Edit3, Lock, Unlock, Star, Copy
- Escape all quotes and apostrophes in JSX text nodes (&quot; / &apos;)
- Add key props to <span> elements used as table cell content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:19:21 +01:00
Vadym Samoilenko
623f82c692 feat: replace user-guide.pdf with interactive searchable page
- New /user-guide Next.js page with sticky TOC, Cmd+K search, section
  highlighting via IntersectionObserver
- 11 sections covering all features: Overview, Login, Navigation, Process
  Helper, Personal Assistant, AI Agents (all 4 creation paths), Knowledge
  Base, Language, Admin Panel, Tips, Troubleshooting
- Role-based content (User / Agent Manager / Content Manager / Super Admin)
  clearly marked with colour badges throughout
- Section 6.9 "Available templates" fetches live from /agents/templates API
  so the list stays current as admins add or remove templates
- Sidebar "Help" button now navigates to /user-guide instead of opening PDF

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:15:06 +01:00
Vadym Samoilenko
996caee994 fix: build AIMessage from parsed_calls to prevent empty tool_use.name
Streaming produces incomplete chunks (empty name/id). Previously assistant_msg
was built from raw tool_calls_in_response, causing Anthropic to reject with
'tool_use.name: String should have at least 1 character'.

Now dedup + parse happens first; assistant_msg is built from the validated
parsed_calls list so only complete tool_use blocks reach the API.
Also return early if all chunks were incomplete (no valid calls).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:04:27 +01:00
Vadym Samoilenko
bd7973b033 feat: seed 10 enterprise agent templates (adapted from ruflo patterns)
Templates: Research Analyst, Code Assistant, Document Writer, Project Planner,
Data Analyst, HR Assistant, Security Auditor, Meeting Assistant,
Content Strategist, SQL & Database Expert.

Each template is is_template=true, visibility=public, status=active.
HR Assistant + Research Analyst have enable_rag=true.
Meeting Assistant + Project Planner get M365 tool_ids.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:02:48 +01:00
Vadym Samoilenko
9cdc370d6d fix: correct CreateAgentTool execute signature + tool_use.id empty string
- agent_builder.py: fix execute(self, arguments, context) signature on
  CreateAgentTool and ListAgentsTool to match BaseTool contract; was
  (self, context, **kwargs) causing "takes 2 positional args but 3 given"
- llm.py: ensure tool call IDs are non-empty before building AIMessage
  so Anthropic API doesn't reject with "tool_use.id: valid string required"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:55:30 +01:00
Vadym Samoilenko
bfdc71bc0d fix: PA create_agent tool not firing — two root causes fixed
1. _should_use_tools classifier didn't know about agent management →
   extended prompt to include 'creating or managing AI agents'
2. Assistant system prompt didn't tell Claude about create_agent tool →
   migration 023 updates DB prompt with explicit instructions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:45:52 +01:00
Vadym Samoilenko
6a47e3bd4f feat: make agent settings button visible — yellow + label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:33:50 +01:00
Vadym Samoilenko
ecb76e7157 feat: agent settings panel in chat — edit/view agent config inline
Right panel (360px) toggles via SlidersHorizontal button in chat header.
Shows full agent config: name, description, category, instructions,
welcome message, suggested prompts, model, capabilities, visibility.
Editable for own/managed agents; read-only + Fork for system agents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:26:37 +01:00
Vadym Samoilenko
2307e625e2 fix: remove claude-opus-4-6 from model list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:22:33 +01:00
Vadym Samoilenko
a1969f61a5 fix: sync model lists to match backend configuration
OpenAI: gpt-5.2 only
Anthropic: claude-sonnet-4-6, claude-opus-4-6, claude-haiku-4-5-20251001
Google: gemini-3.1-pro-preview only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:22:05 +01:00
Vadym Samoilenko
1e5265d0f8 feat: expand agent picker Create Custom form with full settings
Added: AI provider/model selection, welcome message, suggested prompts chip list,
RAG toggle, file upload toggle. Agents are still created as private.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:20:16 +01:00
Vadym Samoilenko
8fad37a9b1 fix: widen mode prop from 'rag'|'assistant' union to string across chat components
Agents are now dynamic — any slug can be passed as mode.
Updated: chat-input, chat-list, chat-message, message-feedback, useChatStore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:15:44 +01:00
Vadym Samoilenko
4f3d614b08 fix: widen ChatList mode prop type from union to string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:06:48 +01:00
Vadym Samoilenko
9b4fb93606 fix: resolve ESLint errors blocking build
- Remove unused isAgentManager var (admin/page.tsx)
- Remove unused BookOpenCheck, Sparkles imports (sidebar.tsx)
- Remove unused Badge import (agent-card.tsx)
- Suppress known useEffect dep warning in admin/page.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:03:50 +01:00
Vadym Samoilenko
5ca1585acc fix: remove react-hook-form, Select, Switch deps — use native elements
Replace react-hook-form with useState, shadcn Select with native <select>,
and Switch with inline toggle buttons. No new packages required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:01:39 +01:00
Vadym Samoilenko
373172a27c fix: migration 022 — use CAST() instead of ::jsonb to avoid asyncpg syntax error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 15:56:19 +01:00
Vadym Samoilenko
349f9c243e feat: agent builder — dynamic agents with admin editor, user catalog, and PA creation
- Backend: Agent model + migration 022 (creates agents table, seeds system agents, backfills conversations, adds What's New announcement)
- Backend: agents CRUD API (/agents/), role-based permission matrix, agent_manager role
- Backend: chat endpoint refactored to resolve agent by slug, use agent's LLM config/tools/RAG
- Backend: PA CreateAgentTool + ListAgentsTool for conversational agent creation
- Backend: LLMFactory extended with get_llm_from_config() + stream_completion_with_llm()
- Frontend: AgentListItem/Agent types, useAgentStore, dynamic sidebar with lucide icon resolution
- Frontend: Admin → Agents tab + full LibreChat-style AgentEditorModal (react-hook-form)
- Frontend: AgentPickerModal (Browse Templates + Create Custom), AgentCard
- Frontend: chat page reads ?agent=slug, shows dynamic header/welcome/prompts from DB

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 15:54:35 +01:00
Vadym Samoilenko
720f8a657c fix: remove unused cn import in whats-new-modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 15:14:24 +01:00
Vadym Samoilenko
53984e4a27 feat: whats-new shows only unread items + feedback announcements
- Modal now marks all as read on open and only shows new items;
  once seen they never reappear (no more grayed-out clutter)
- Empty state shows "You're all caught up!" message
- Migration 021: seeds two announcements — one for users (rate answers)
  and one for admins/content managers (feedback dashboard)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 15:13:04 +01:00