52 Commits

Author SHA1 Message Date
Carlos Garcia
5261396ef7 fix(agent): add missing ping() to OllamaBackend and OdooClient
Health endpoint called .ping() on both but neither implemented it,
causing ollama/odoo to always show as error and the bot to stay offline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 22:51:03 -04:00
Carlos Garcia
103f575f92 fix(addon): bot presence — fix network isolation, computed field write, and timer gap
- docker-compose.odoo.yml: add activeblue-net so Odoo can reach
  activeblue-agent by hostname; fix addons volume mount (was odoo_module)
- ab_ai_bot.py: bus.presence.status is computed — write only last_poll
  and last_presence; set last_poll 90s ahead when online so the bot
  stays green across the 60s cron cycle (DISCONNECTION_TIMER=30s)
- ir_cron.xml: reduce ping interval to 20s (uses Odoo 18 seconds type)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 22:42:35 -04:00
f30751a696 fix: correct server IP 192.168.2.47 -> 192.168.2.9 in MCP config 2026-05-12 23:09:04 +00:00
9b2dc1ee6b fix: publish port 8001 to host, replace curl healthcheck with python3 urllib 2026-05-12 23:09:03 +00:00
b6d5e6ee57 fix: add AgentRegistry.get_all() method
Routers calling /registry/agents raised AttributeError because
get_all() was not defined. Added method returning all registered
agents with active status, capabilities and instance flags.
2026-05-12 23:08:45 +00:00
Carlos Garcia
c215b9bf0c fix(addon): require LLM backend reachability for bot online state
action_ping was hitting /health, which returns 200 as long as the
FastAPI app responds — even when Ollama is down, dispatch fails. So the
bot showed online while every DM errored.

Switch to /health/detailed and gate 'online' on db, master_agent and
the active LLM backend (ollama for local privacy mode, claude
otherwise) all reporting 'ok'. Anything else flips the bot to error
or offline, which propagates through _sync_bot_user_presence to a grey
dot in Discuss.
2026-04-26 11:32:35 -04:00
Carlos Garcia
38fa508e0f fix(addon): correct existence check for bus.presence model
env.get() isn't an Odoo Environment method, so the existence guard
silently returned without ever writing the bot's presence row. Use the
'in self.env' check instead.
2026-04-25 17:10:35 -04:00
Carlos Garcia
5e9b4b244c feat(addon): show AI bot online in Discuss when agent is healthy
Extend cron_ping_all to upsert a bus.presence row for the
activeblue_ai_bot user, mirroring the agent service's reachability:
green dot when /health responds 200, offline otherwise. Drop the cron
interval from 5m to 1m so the presence stays fresh (Odoo's presence
client expects refreshes well under a minute).
2026-04-25 17:05:55 -04:00
Carlos Garcia
d49a51a5e8 fix(agent): tolerant intent JSON parse + log raw output on failure
The classifier was silently falling back to a clarification prompt every
time the LLM wrapped its JSON in markdown fences, prefixed it with
'json', or added surrounding prose. The bot then asked 'Could you
clarify what you need?' to every message regardless of clarity.

Now: strip code fences, slice to the first {...} block, and on parse
failure log the raw content (truncated) and treat the message as 'no
specialist agent' so the direct-answer fallback responds instead of
looping on clarification.
2026-04-24 23:28:18 -04:00
Carlos Garcia
f774cca7ab feat(agent): direct-answer fallback for non-Odoo questions
Previously when the LLM classified a message as needing no specialist
agent, the dispatcher built zero directives and _synthesize returned
'No agent responses received.' Greetings, follow-up clarifications,
and general questions all fell into this dead end.

Now when intent.agents is empty and no clarification is needed, the
master makes a second LLM call with the recent conversation as context
and answers directly. Updated master_system.txt to steer the classifier
toward agents=[] for chitchat instead of forcing a clarification loop.
2026-04-24 23:27:06 -04:00
Carlos Garcia
27325bc140 fix(agent): render denied_agents list in access error
The f-string only spanned the first fragment ('You don') so the
{chr(44).join(...)} placeholder leaked into chat output as literal
text. Build the message with plain string concat.
2026-04-24 23:25:58 -04:00
Carlos Garcia
18f2c91715 fix(agent): persist user message on every turn, not just happy path
User messages were only saved inside _update_memory at the end of a
successful directive. The clarification and access-denied branches
returned early without ever calling it, so when a clarification turn
asked 'what do you mean?' and the user replied, the original question
was missing from context — the bot looked at a transcript of nothing
but its own clarifying questions and asked yet another.

Save the user message at the top of handle_message so every branch
includes it. Drop the now-duplicate write from _update_memory.
2026-04-24 23:24:40 -04:00
Carlos Garcia
01adfbfb1a fix(agent): handle dict and pydantic shapes from ollama-python
ollama-python 0.3.x returns the response as a dict, while newer releases
return pydantic objects. The backend assumed objects (response.message)
and crashed with AttributeError on every dispatch. Use a helper that
accepts either shape so the code works across versions.
2026-04-24 23:16:36 -04:00
Carlos Garcia
f020d1406a deps(agent): add ollama python client
ollama_backend.py imports ollama lazily; without the package every
local-mode dispatch crashed with ModuleNotFoundError.
2026-04-24 23:13:34 -04:00
Carlos Garcia
67e6eff534 fix(agent): use plain substitution for master_system prompt
The prompt template contains a literal JSON example block ({"needs_clarification": ...})
which str.format() tried to interpret as format fields, raising KeyError on every
Discuss DM. Switch to .replace() so braces in the template are taken literally.
2026-04-24 23:12:51 -04:00
Carlos Garcia
4cbc4cc0f1 chore(agent): log full traceback when MasterAgent fails
Without exc_info we only see the bare exception string, which has been
unhelpful for debugging Discuss DM failures (e.g. a KeyError whose
message is just a JSON key, with no clue where it was raised).
2026-04-24 23:11:46 -04:00
Carlos Garcia
b4f1f5f015 fix(agent): coerce user_id to int in MasterAgent.handle_message
Odoo's bot model serialises user_id as a string (str(uid)) over the
HTTP boundary, but the asyncpg memory queries ($1) expect an integer.
This caused 'str object cannot be interpreted as an integer' on every
Discuss DM. Cast at the entry point so downstream stores get an int.
2026-04-24 23:10:00 -04:00
Carlos Garcia
4cb94b18f1 fix(agent): align /dispatch with MasterAgent.handle_message signature
The router was calling handle_message(user_id, message, context, session_id)
but MasterAgent accepts (user_id, channel_id, message, directive_id) and
returns MasterResponse{response, status, ...} with no .reply or
.agent_reports fields. Discuss DMs to the bot crashed with TypeError.

Now the router:
- Derives directive_id from session_id (or generates one)
- Pulls channel_id out of req.context
- Maps MasterResponse.response -> DispatchResponse.reply
- Returns an empty agent_reports list (the field is reserved for future use;
  per-agent reports aren't part of MasterResponse)
2026-04-24 23:06:24 -04:00
Carlos Garcia
beb08b0b4b fix(addon): seed ab.ai.bot and agent registry on install
The bot DM flow silently failed because ab_ai_mail.message_post override
bails when no active ab.ai.bot row exists, and the agent service loaded
0 agents from an empty ab.ai.agent.registry. Both tables stayed empty
because nothing populated them.

The post_init/post_migrate hook now seeds:
- One active ab.ai.bot pointing at http://activeblue-agent:8001
- The 8 specialist agents (finance, accounting, crm, sales, project,
  elearning, expenses, hr) plus master, all on the ollama backend

Idempotent: skips rows that already exist.
2026-04-24 22:51:37 -04:00
Carlos Garcia
79537a5e21 fix(addon): create AI bot as Confirmed (skip invitation email)
The post_init/post_migrate hook now:
- Uses no_reset_password context to skip Odoo's invitation email flow
- Writes a res_users_log row so the user shows as Confirmed instead of
  Pending Invitations (Odoo 18 derives state from log presence)
- Idempotently ensures the partner_activeblue_ai external ID points at
  the bot's partner so env.ref() resolves in ab_ai_mail.py

Discovered while fixing the bot DM flow on miaai: even after the user
was created, Discuss showed it as Pending until a res_users_log row was
inserted manually.
2026-04-24 22:49:42 -04:00
Carlos Garcia
368c50bde4 fix(registry): use correct Odoo field names (active/agent_name not is_active/agent_key)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:29:45 -04:00
Carlos Garcia
01f7037a38 fix(addon): create AI bot as internal user; use post_migrate_hook
- Bot needs to be a res.users to appear in Discuss DM search
- post_migrate_hook runs on both install and -u update
- Idempotent: skips creation if user already exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:20:09 -04:00
Carlos Garcia
d5381220fb fix(addon): create bot partner via post_init_hook instead of XML
XML record creation bypasses ORM defaults causing NOT NULL violation
on autopost_bills (added by account module). Hook uses ORM create()
which applies all field defaults correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:17:43 -04:00
Carlos Garcia
65957339ef fix(addon): inherit discuss.channel not mail.channel (renamed in Odoo 17+)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:15:46 -04:00
Carlos Garcia
e3ef649ee6 fix(addon): remove discuss from depends (included in mail in Odoo 18)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:15:08 -04:00
Carlos Garcia
992d2c2775 feat(addon): add ActiveBlue AI bot to Odoo Discuss
- Create res.partner for the AI bot (appears in DM contacts)
- Override mail.channel.message_post to intercept direct messages
  to the bot partner and forward them to the agent service
- Post the agent reply back into the Discuss channel as the bot
- Add discuss to depends; load res_partner_bot.xml data

Users can now open Discuss -> New Message -> search 'ActiveBlue AI'
to start a conversation with the agent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 22:14:00 -04:00
Carlos Garcia
6cb09282c2 fix(addon): replace useService('rpc') with direct rpc import for Odoo 18
rpc service was removed in Odoo 17+. Import rpc function directly from
@web/core/network/rpc instead of using useService('rpc').

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 21:44:59 -04:00
Carlos Garcia
86708f09eb fix(addon): replace deprecated attrs with Odoo 17+ inline expressions
attrs={'invisible': [...]} syntax was removed in Odoo 17.
Converted all occurrences to inline invisible= expressions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 21:23:15 -04:00
Carlos Garcia
15afe51d9c fix(addon): resolve all Odoo 18 install errors
- security/res_groups.xml: fully qualify ref('group_ai_user') with module prefix
- data/ir_cron.xml: remove numbercall field (removed in Odoo 17/18)
- views/menus.xml: remove web_icon referencing missing static/src/img/icon.png
- controllers/webhook.py: use get_json() instead of deprecated json.loads(data)
- security/ir.model.access.csv: use fully-qualified group external IDs (prior commit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 21:20:40 -04:00
Carlos Garcia
a4759b583e fix(addon): use fully-qualified external IDs in access CSV
group_ai_manager -> activeblue_ai.group_ai_manager to ensure Odoo
resolves the group reference correctly regardless of load order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 19:23:36 -04:00
Carlos Garcia
cd606a1db8 fix(addon): load res_groups.xml before ir.model.access.csv
CSV references group_ai_manager and group_ai_user which must be
created before the access rules that reference them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 19:21:40 -04:00
Carlos Garcia
590f1b7ee2 fix: make Odoo login configurable via ODOO_USER (default __system__)
Some Odoo instances require the user's actual login/email for API key
auth rather than the __system__ special login. ODOO_USER defaults to
__system__ for standard Odoo 16+ installs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 19:15:06 -04:00
Carlos Garcia
65e471b6ce fix: MemoryManager kwarg 'llm' -> 'llm_router'; fix alembic script_location
- main.py: MemoryManager(pool=pool, llm=...) -> llm_router=...
  Class signature is __init__(self, pool, llm_router=None).

- alembic.ini: script_location = migrations -> agent_service/migrations
  When alembic runs from WORKDIR /app inside the container, 'migrations'
  resolves to /app/migrations (missing). Correct path is
  /app/agent_service/migrations where versions/ actually lives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 17:43:03 -04:00
Carlos Garcia
6d15859779 fix(setup.sh): detect stale DB volume, run Alembic migrations on startup
- Before bringing up the stack, check if the agent-db volume exists but
  is missing the expected database (left from a previous broken run with
  wrong POSTGRES_DB). Offer to wipe and re-init automatically.
- After docker compose up, wait for pg_isready then run
  `alembic upgrade head` inside the agent container so tables are created
  before the agent attempts to use them.
- Restart agent-service after migrations so it connects to a fully
  initialized database on its first attempt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 17:39:51 -04:00
Carlos Garcia
7765824c70 feat: add setup.sh — zero-config interactive installer
Replaces manual .env editing with a guided setup script.

Auto-discovers:
- Odoo container name and database (via `docker ps` + odoo.conf inspection)
- Ollama endpoint (scans 192.168.2.10/9, 192.168.2.1, localhost on port 11434)
- Ollama model list (lets user pick from available, auto-selects if only one)

Auto-generates (idempotent — preserves on re-run):
- POSTGRES_PASSWORD (openssl rand -hex 24)
- AGENT_API_KEY     (openssl rand -hex 32)
- WEBHOOK_SECRET    (openssl rand -hex 32)

Postgres constants always written correctly:
- POSTGRES_HOST=agent-db, POSTGRES_DB=activeblue_ai, POSTGRES_USER=activeblue
  (fixes previous issue where these were blank or wrong in .env)

Odoo API key:
- Attempts auto-creation via Odoo HTTP session + res.users.apikeys.description
  wizard (works on Odoo 16/17/18 with valid admin credentials)
- Falls back to clear manual instructions + paste prompt on failure

Writes .env with chmod 600. Offers to `docker compose up -d` when done.

Usage:
  cd /root/odoo/odoo-ai
  bash setup.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 17:35:23 -04:00
Carlos Garcia
a3fbc9746f fix: remove stale port binding and deprecated version key from compose
- Remove ports: mapping (192.168.2.47:8001:8001 — stale IP from prior host,
  caused 'cannot assign requested address' bind failure). Agent is only
  reachable via activeblue-net; no host exposure needed.
- Remove top-level version: key (obsolete, triggers deprecation warning).

These fixes were applied manually on the miaai host but never committed,
causing git pull to conflict and the Python fixes to silently not apply.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:53:54 -04:00
Carlos Garcia
c769fca79f fix: resolve all 5 startup constructor errors + add DB retry
Fixes all errors reported in docker compose logs agent-service:

1. config.py: add ollama_max_concurrent, claude_timeout, claude_max_concurrent
   fields so LLMRouter(config=settings) can read them without AttributeError.

2. main.py - LLM router: drop manual OllamaBackend/ClaudeBackend construction;
   call LLMRouter(config=settings, pg_pool=pool) to match class signature.
   Fixes: OllamaBackend.__init__() unexpected kwarg 'base_url'.

3. main.py - DB: add 5-attempt retry with 2s backoff and redacted DSN logging.
   Fixes: connection refused race on startup before Postgres accepts connections.

4. main.py - AgentRegistry: call AgentRegistry() with no args (class takes none),
   then await agent_registry.load_from_odoo(odoo) to populate active agents.
   Fixes: AgentRegistry.__init__() unexpected kwarg 'odoo'.

5. main.py - PeerBus: pass registry=agent_registry at construction; register
   specialist agents on agent_registry (not peer_bus, which has no register()).
   peer_bus.py: make directive_id optional (default None) — bus is a singleton
   at startup; directive_id is only needed per-request.
   Fixes: PeerBus.__init__() missing positional args 'registry' and 'directive_id'.

6. main.py - MasterAgent: drop unexpected peer_bus= kwarg from constructor call.
   Fixes: MasterAgent.__init__() unexpected kwarg 'peer_bus'.

7. mcp_router.py: pass NotificationOptions() instance instead of None.
   Fixes: AttributeError 'NoneType' has no attribute 'tools_changed' (was applied
   in running container but not committed; now committed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:48:23 -04:00
ActiveBlue Build
66b114cdcf feat(mcp): add MCP gateway — 14 tools over SSE, all agent calls forced local
Architecture:
- agent_service/mcp/tools.py: 14 Tool definitions with JSON schemas
    dispatch, finance_query, accounting_query, crm_query, sales_query,
    project_query, elearning_query, expenses_query, employees_query,
    get_health, list_agents, trigger_sweep, get_pending_approvals, approve_directive
- agent_service/mcp/server.py: mcp.Server with list_tools + call_tool handlers
- agent_service/routers/mcp_router.py: Starlette routes at /mcp/sse + /mcp/messages
- main.py: mounts MCP routes alongside existing FastAPI routers (graceful fallback if mcp not installed)

Privacy guarantee (enforced in server.py, not by convention):
- _force_local_context() sets llm_router._privacy_mode = 'local' before EVERY agent call
- _restore_mode() restores original mode after the tool returns
- HIPAA agents (finance, accounting, expenses, employees) were already Ollama-only;
  MCP adds a second enforcement layer for all 8 agents
- MCP client (e.g. Claude Code CLI) receives only tool results — no LLM completions cross the boundary

Usage (Claude Code CLI):
  claude mcp add --transport sse http://192.168.2.47:8001/mcp/sse
  or copy claude_mcp_config.json to ~/.claude/mcp_servers.json

requirements.txt: added mcp==1.3.0
tests/test_mcp_server.py: 13 tests covering tool count, schemas, HIPAA labelling, privacy override

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 16:45:49 -04:00
ActiveBlue Build
fb4bf56816 feat(packaging): add Debian packaging and APT repository scripts
debian/DEBIAN/control: package metadata, depends on python3.11+, postgresql-client
debian/DEBIAN/postinst: creates activeblue-ai system user, installs venv, enables service
debian/DEBIAN/prerm: stops and disables service before removal
debian/DEBIAN/postrm: purge removes config, logs, venv, and system user
debian/lib/systemd/system/activeblue-ai.service:
  - Runs as dedicated user with PrivateTmp + ProtectSystem hardening
  - EnvironmentFile=/etc/activeblue-ai/.env
  - Restart=on-failure with 5s backoff
debian/usr/bin/activeblue-ai: CLI with start/stop/restart/status/logs/migrate/health/sweep/privacy/version
build_deb.sh: builds activeblue-ai_X.Y.Z_all.deb in dist/
publish_repo.sh: scans packages, generates Release + checksums, optional GPG signing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:09:48 -04:00
ActiveBlue Build
7487fc73f9 feat(infra): add sweep coordinator, structured logging, test suite, and README
Sweep coordinator (Step 16):
- SweepCoordinator runs all 8 agents in parallel with 60s per-agent / 300s total timeout
- Aggregates findings, actions, errors into SweepCoordinatorResult
- Registered in FastAPI lifespan; triggered via POST /sweep

Structured logging (Step 18):
- logging_utils/structured.py: JSONFormatter emitting ts/level/logger/msg + custom fields
- log_directive_event() for structured directive lifecycle logging
- push_to_loki() async Loki push (graceful no-op if LOKI_URL unset)
- configure_logging() replaces root handler at startup

Tests (Steps 17+19):
- conftest.py: mock_odoo, mock_pool, mock_llm fixtures
- test_tool_validator.py: 9 tests covering validation, coercion, hallucination stripping
- test_llm_router.py: 6 tests covering local/cloud/hybrid modes and HIPAA enforcement
- test_peer_bus.py: 6 tests covering registration, timeout, depth, circular detection
- test_finance_agent.py: 10 tests covering all 6 steps + sweep + peer request
- test_memory_manager.py: 3 tests covering context build + hard cap enforcement
- test_dispatch_router.py: 3 tests covering dispatch, rate limit, health endpoint
- test_odoo_client.py: 4 tests covering search_read, write result, unlink warning
- test_e2e_dispatch.py: 2 E2E tests - full dispatch cycle + peer bus communication

README (Step 20): architecture diagram, privacy modes, quick start, env vars, structure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:08:11 -04:00
ActiveBlue Build
fe47f950e4 feat(agents): add 7 specialist agents with tools and system prompts
Agents (all following 6-step contract: _plan/_gather/_reason/_act/_report):
- AccountingAgent: trial balance, chart of accounts, tax summary (HIPAA-locked)
- CrmAgent: pipeline summary, lead/opportunity management, won/lost analysis
- SalesAgent: sales orders, quotations, revenue by rep, expired quote detection
- ProjectAgent: task tracking, blocked/overdue detection, timesheet logging
- ElearningAgent: course completion, low-engagement flagging, next-course suggestion
- ExpensesAgent: expense sheets, pending approvals, policy violations (HIPAA-locked)
- EmployeesAgent: headcount, contracts, leaves, attendance, expired contract sweep (HIPAA-locked)

Tools (one file per domain):
- accounting_tools.py, crm_tools.py, sales_tools.py, project_tools.py
- elearning_tools.py, expenses_tools.py, employees_tools.py

System prompts: each agent has a domain-specific system.txt with rules and output format

All agents implement handle_peer_request() and sweep() for proactive monitoring
HIPAA-locked agents (accounting, expenses, employees) enforced via LLMRouter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:04:32 -04:00
ActiveBlue Build
29409ed71d feat(odoo): add activeblue_ai Odoo 18 module with OWL2 frontend
Models:
- ab.ai.bot: service URL, webhook secret, privacy mode, ping/dispatch
- ab.ai.directive: full directive lifecycle log with status tracking
- ab.ai.log: activity log with level/agent/record linkage
- ab.ai.agent.registry: agent list synced from agent service

Controllers:
- webhook.py: /ai/webhook/callback handles directive_completed, escalation, sweep_findings
- health_proxy.py: /ai/health proxies agent service detailed health
- approval.py: /ai/chat dispatch, /ai/approval/pending, /ai/approval/respond

Security:
- group_ai_user (chat) + group_ai_manager (configure, approve, logs)
- ir.model.access.csv for all 4 models

Views: list/form for bot, directives, logs, registry; main menu with AI brain icon

OWL2 frontend:
- systray_button.js: brain icon in top bar, status dot, pending approval badge
- ai_panel.js: slide-in chat panel, approval workflow, 30s poll for pending items
- CSS: slide-in animation, message bubbles, loading dots, approval section

Data: 4 cron jobs (ping, registry sync, directive/log cleanup)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 17:59:02 -04:00
ActiveBlue Build
430ab966b2 feat(service): add FastAPI agent service with 5 routers and Docker setup
- config.py: pydantic-settings with all env vars, privacy mode, per-agent overrides
- app_state.py: global singletons (pool, master agent, registry, llm_router, sweep)
- main.py: FastAPI lifespan startup — DB pool, LLM router, Odoo client, agents, master
- routers/dispatch.py: POST /dispatch with rate limiting and webhook secret auth
- routers/approval.py: GET /approval/pending, POST /approval/respond
- routers/registry.py: GET/POST /registry/agents, POST /registry/backend overrides
- routers/sweep.py: POST /sweep trigger, GET /sweep/status
- routers/health.py: GET /health, GET /health/detailed (DB/Odoo/Ollama checks)
- requirements.txt: pinned deps (fastapi, uvicorn, asyncpg, anthropic, alembic)
- Dockerfile: python:3.11-slim, single uvicorn worker
- docker-compose.yml: agent-service + postgres:15, bound to 192.168.2.47:8001

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 17:54:28 -04:00
ActiveBlue Build
dab6354d09 feat(finance): add FinanceAgent with full 6-step contract and FinanceTools
- FinanceAgent implements _plan/_gather/_reason/_act/_report lifecycle
- Proactive sweep flags 30+ day overdue invoices, auto-sends reminders >60d/>$1k
- PeerBus handler exposes overdue_summary, payment_history, financial_summary
- HIPAA-locked: Ollama only, no cloud LLM routing
- FinanceTools wraps OdooClient with 9 read/write methods on account.move
- finance_system.txt prompt enforces no-write-to-invoices rule
- research/finance_research.md documents Odoo 18 account model details

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 17:51:49 -04:00
ActiveBlue Build
4ca62ee54b feat: add Master AI with directive builder and synthesis 2026-04-12 17:17:44 -04:00
ActiveBlue Build
21998f76aa feat: add base agent and peer bus 2026-04-12 16:55:30 -04:00
ActiveBlue Build
9ab1f8bbf8 feat: add 3-tier memory layer 2026-04-12 16:51:39 -04:00
ActiveBlue Build
7d92c2ea6f feat: add LLM abstraction layer (router, Ollama backend, Claude backend) 2026-04-12 16:46:18 -04:00
ActiveBlue Build
0e13b93e3a feat: add Odoo XML-RPC client with connection pool and retry 2026-04-12 16:46:13 -04:00
ActiveBlue Build
34a65a485e chore: add alembic migrations for memory and directive schema 2026-04-12 14:18:07 -04:00