updates on the agent flow

This commit is contained in:
2026-09-24 19:25:19 +05:30
parent cbce47f949
commit 9c50de1142
15 changed files with 3998 additions and 1 deletions

294
src/lib/agentNetwork.js Normal file
View File

@@ -0,0 +1,294 @@
// ==============================|| The AI_engine agent swarm ||============================== //
//
// Nine agents — JARVIS plus eight specialised — living in the `AI_engine`
// repository, not in this one and not in `doormile_backend`. They subscribe to
// the same NATS JetStream the Go backend publishes to, reason about what they
// see, and raise alerts. Every autonomy gate is off, so none of them writes to
// the system.
//
// ─── Why this is a static table ──────────────────────────────────────────────
//
// `AI_engine` exposes no endpoint this console can call. There is a FastAPI
// gateway inside it with a `GET /agents/status`, but it is not deployed beside
// the console and nothing routes to it.
//
// So this file is a hand-maintained record, and it is deliberately shaped like
// the response such an endpoint would return — one flat object per agent, the
// same keys — so swapping it for a fetch is a change of source, not a rewrite
// of the page.
//
// **It can go stale.** Every `lastSeen` here was read from
// `AI_engine/logs/logiflow_2026-09-16.log`, the most recent run, which covers
// 16–20 Sep 2026. Treat the counts as of that date, not as live. The page says
// so on screen, because a status board that looks live and is not is worse than
// one that admits its age.
/** When the figures below were taken. Rendered on the page — see the note above. */
export const SNAPSHOT_AT = '2026-09-20T19:51:23+05:30';
export const SNAPSHOT_LABEL = 'Last run · 16–20 Sep 2026';
/**
* How an agent is woken.
*
* The split matters more than it looks: `nats` agents subscribe to the bus
* themselves and can act the moment an event lands, while `task` agents only
* run when JARVIS hands them something — and nothing in the Go backend asks
* JARVIS for anything, which is why five of the nine have never run at all.
*/
export const TRIGGER = {
nats: 'NATS event',
natsPull: 'NATS pull',
task: 'JARVIS task',
internal: 'In-process',
};
export const AGENTS = [
{
id: 'JARVIS',
name: 'JARVIS',
domain: 'orchestration',
status: 'running',
trigger: 'internal',
file: 'core/agent.py',
lines: null,
tests: null,
events: 0,
load: 8,
blurb: 'Master agent. Every other agent registers under it and receives work through it.',
detail:
'The orchestrator. It holds the registry of the eight specialised agents and routes tasks to them over an in-process message bus. It never touches NATS itself.',
subscribes: [],
publishes: 'Tasks to the five task-driven agents.',
api: 'No — it delegates.',
autonomy: 'not gated — routes only',
gated: false,
lastSeen:
'Started cleanly on 16 Sep at 15:23 and registered eight sub-agents. It has routed nothing since, because nothing in the backend asks it to.',
},
{
id: 'DISPATCH_AGENT',
name: 'Dispatch',
domain: 'dispatch',
status: 'active',
trigger: 'nats',
file: 'agents/dispatch_agent.py',
lines: 508,
tests: true,
events: 2,
load: 62,
blurb:
'Counts assignment failures per geo cell, checks rider liveness, raises an ops alert when a zone stops getting covered.',
detail:
'The only agent doing real work. It watches every assignment outcome, buckets failures by geographic cell, and decides whether a zone has genuinely lost coverage or is simply quiet. Escalation is rate-limited to one alert per zone per day.',
subscribes: ['booking.assigned', 'booking.assignment_failed'],
publishes: 'Ops alerts, customer delay notices, escalations.',
api: 'No — reads presence from Redis.',
autonomy: 'DISPATCH_AGENT_AUTONOMOUS = false',
gated: true,
lastSeen:
'Bound to stream ASSIGNMENTS on 16 Sep, and saw two assignment failures three seconds later. Both landed in zone unknown — the backend does not publish zone_id, which is exactly why a later commit started bucketing failures by geo cell instead.',
},
{
id: 'EXCEPTION_AGENT',
name: 'Exception',
domain: 'exception_handling',
status: 'degraded',
trigger: 'natsPull',
file: 'agents/exception_agent.py',
lines: 818,
tests: true,
events: 0,
load: 100,
blurb: 'Stall detection. Spots a rider who has stopped moving or a booking that has stopped progressing.',
detail:
'The largest agent. Two pull loops read rider pings and stall events, and a sweep walks active bookings looking for ones that have gone quiet. When it finds a stalled rider it publishes miler.stalled for the dispatch side to act on.',
subscribes: ['miler.location.updated', 'miler.stalled'],
publishes: 'miler.stalled',
api: 'Yes — api_post.',
autonomy: 'EXCEPTION_AGENT_AUTONOMOUS = false',
gated: true,
lastSeen:
'Lost NATS and never recovered. Both pull loops logged the same connection-closed error every two seconds for four days — 17,188 of the 18,146 lines in the log. The loop sleeps and retries a dead handle; it has no reconnect.',
},
{
id: 'EXPRESS_DISPATCH_AGENT',
name: 'Express Dispatch',
domain: 'express_dispatch',
status: 'idle',
trigger: 'nats',
file: 'agents/express_dispatch_agent.py',
lines: 391,
tests: true,
events: 0,
load: 0,
blurb: 'Handles dispatch requests raised from the console and express booking path.',
detail:
'The console-side counterpart to Dispatch. It waits on express.dispatch_req and works a booking the express path could not place, reading and writing through the Go API rather than assigning directly.',
subscribes: ['express.dispatch_req'],
publishes: 'Dispatch outcomes back onto the bus.',
api: 'Yes — api_get, api_post.',
autonomy: 'EXPRESS_AGENT_AUTONOMOUS = false',
gated: true,
lastSeen: 'Bound its consumer on 16 Sep and has received nothing. No express dispatch request has ever been published.',
},
{
id: 'ORDER_AGENT',
name: 'Order',
domain: 'order_management',
status: 'idle',
trigger: 'task',
file: 'agents/order_agent.py',
lines: 348,
tests: false,
events: 0,
load: 0,
blurb: 'Order intake, validation and categorisation.',
detail:
'Validates an incoming order, classifies it by zone and service type, and patches the result back. The most connected of the task agents — it reads, creates and patches through the Go API.',
subscribes: [],
publishes: 'Nothing on the bus.',
api: 'Yes — get, post, patch.',
autonomy: 'no gate — never reachable',
gated: false,
lastSeen: 'Started on 16 Sep. Zero tasks received, because nothing sends tasks.',
},
{
id: 'ROUTE_OPTIMIZER',
name: 'Route Optimizer',
domain: 'route_optimization',
status: 'idle',
trigger: 'task',
file: 'agents/route_optimizer_agent.py',
lines: 465,
tests: false,
events: 0,
load: 0,
blurb: 'Builds delivery routes from zones and available hubs. Ten-minute cache.',
detail:
'Sequences waypoints into routes using zone membership and hub availability. This is the piece the Go backend has no equivalent for — HubBatchAssign decides who gets a booking, never what order a rider runs their stops in.',
subscribes: [],
publishes: 'Nothing on the bus.',
api: 'No.',
autonomy: 'no gate — never reachable',
gated: false,
lastSeen: 'Started on 16 Sep. Zero tasks received.',
},
{
id: 'HUB_AGENT',
name: 'Hub',
domain: 'hub_management',
status: 'idle',
trigger: 'task',
file: 'agents/hub_agent.py',
lines: 460,
tests: false,
events: 0,
load: 0,
blurb: 'Hub operations — transit records, capacity, what is sitting where.',
detail:
'Tracks inventory at each hub and the transit records moving between them, and answers capacity questions about whether a hub can absorb more volume.',
subscribes: [],
publishes: 'Nothing on the bus.',
api: 'No.',
autonomy: 'no gate — never reachable',
gated: false,
lastSeen: 'Started on 16 Sep. Zero tasks received.',
},
{
id: 'FLEET_AGENT',
name: 'Fleet',
domain: 'fleet_management',
status: 'idle',
trigger: 'task',
file: 'agents/fleet_agent.py',
lines: 349,
tests: false,
events: 0,
load: 0,
blurb: 'Vehicles: status, capacity and live tracking.',
detail:
"Holds vehicle state and assignment, and answers which vehicle can carry a given load. It overlaps with the Go backend's own Vehicle model, which is the live source today.",
subscribes: [],
publishes: 'Nothing on the bus.',
api: 'No.',
autonomy: 'no gate — never reachable',
gated: false,
lastSeen: 'Started on 16 Sep. Zero tasks received.',
},
{
id: 'CUSTOMER_AGENT',
name: 'Customer',
domain: 'customer_communication',
status: 'idle',
trigger: 'task',
file: 'agents/customer_agent.py',
lines: 407,
tests: false,
events: 0,
load: 0,
blurb: 'Customer-facing notifications and tracking updates.',
detail:
'Composes and sends customer notifications across channels for booking, pickup and delivery events. Worth knowing before it is switched on: the backend has no SMS provider registered, so anything it sends by text goes to a log.',
subscribes: [],
publishes: 'Nothing on the bus.',
api: 'Yes — api_get, api_post.',
autonomy: 'no gate — never reachable',
gated: false,
lastSeen: 'Started on 16 Sep. Zero tasks received.',
},
];
/** What the Go backend publishes, and the bus it publishes onto. */
export const SOURCE_SUBJECTS = ['booking.assigned', 'booking.assignment_failed', 'miler.location.updated'];
/**
* The last run, as the log tells it.
*
* `count` is how many times a line repeated. Two of these repeated 8,594 times
* each, which is the whole story of that run and the reason the repeat count is
* on screen rather than in a tooltip.
*/
export const RUN_LOG = [
{ at: '16 Sep 15:23:20', level: 'INFO', tone: 'info', count: null,
text: "DispatchAgent bound 'booking.assigned' on stream 'ASSIGNMENTS'" },
{ at: '16 Sep 15:23:20', level: 'INFO', tone: 'info', count: null,
text: '9 agents initialized (JARVIS + 8 specialized)' },
{ at: '16 Sep 15:23:23', level: 'INFO', tone: 'warn', count: 2,
text: '[DISPATCH] zone unknown — failure count 1/3, 2/3 (backend publishes no zone_id)' },
{ at: '20 Sep 19:51:23', level: 'ERROR', tone: 'error', count: 8594,
text: 'Stalled pull loop error: nats: connection closed' },
{ at: '20 Sep 19:51:23', level: 'ERROR', tone: 'error', count: 8594,
text: 'Location pull loop error: nats: connection closed' },
];
export const LOG_TOTAL_LINES = 18146;
export const LOG_ERROR_LINES = 17188;
/** Agents that subscribe to the bus themselves, in the order they appear. */
export const eventDrivenAgents = () => AGENTS.filter((a) => a.trigger === 'nats' || a.trigger === 'natsPull');
/** Agents that only run when JARVIS hands them work. */
export const taskDrivenAgents = () => AGENTS.filter((a) => a.trigger === 'task');
export const agentById = (id) => AGENTS.find((a) => a.id === id) || AGENTS[0];
/**
* The headline numbers.
*
* Derived rather than written down, so a change to the table above cannot leave
* a stat contradicting the cards underneath it — which is how a dashboard ends
* up saying "3 active" over three grey dots.
*/
export const networkStats = (agents = AGENTS) => {
const working = agents.filter((a) => a.status === 'active').length;
const gated = agents.filter((a) => a.gated).length;
return {
total: agents.length,
working,
degraded: agents.filter((a) => a.status === 'degraded').length,
idle: agents.filter((a) => a.status === 'idle').length,
events: agents.reduce((sum, a) => sum + (a.events || 0), 0),
gated,
autonomyOn: 0,
};
};