305 lines
23 KiB
Go
305 lines
23 KiB
Go
package registry
|
|
|
|
import "doormile/models"
|
|
|
|
// The registry as the code actually stands (verified 2026-09-29). Every entry
|
|
// says what the code DOES, not what a doc claims: an agent that only simulates
|
|
// is seeded as "simulation", and the console must show that badge. Source of
|
|
// this inventory: krow_talent_app/docs/agent-platform-plan.md §2.
|
|
//
|
|
// Upserted on every boot by Seed. Changing a code-defined field here changes
|
|
// it in the database on the next start; operator-owned fields (skill enabled
|
|
// and thresholds, agent autonomous and model) are only written on first insert.
|
|
//
|
|
// Deliberately NOT seeded:
|
|
// - AI_engine's ask_question, order_intake_skill and repeat_run_skill —
|
|
// registered in core/tool_registry.py but never loaded in production.
|
|
// - The four Krow workforce-training tools the console mock carried.
|
|
// - ORDER_AGENT's crmbooking calls — that route was renamed to
|
|
// expressbooking, so they reach nothing.
|
|
// - /internal/agent-decisions — an append-only log the Go side writes, not a
|
|
// capability any skill chooses to use.
|
|
|
|
// Agent statuses and runtimes. The console renders these verbatim.
|
|
const (
|
|
StatusLive = "live"
|
|
StatusPartial = "partial"
|
|
StatusSimulation = "simulation"
|
|
StatusBroken = "broken"
|
|
StatusUnmerged = "unmerged"
|
|
StatusRetired = "retired"
|
|
|
|
RuntimeEngine = "engine"
|
|
RuntimeConsole = "console"
|
|
|
|
KindRead = "read"
|
|
KindWrite = "write"
|
|
KindNotify = "notify"
|
|
// KindEvent is an internal bus event or record: no effect on an order, a
|
|
// rider or a customer by itself, so it is never behind confirmation.
|
|
KindEvent = "event"
|
|
|
|
SourceEngine = "engine"
|
|
SourceConsole = "console"
|
|
SourceCustom = "custom"
|
|
)
|
|
|
|
var (
|
|
validStatuses = map[string]bool{StatusLive: true, StatusPartial: true, StatusSimulation: true, StatusBroken: true, StatusUnmerged: true, StatusRetired: true}
|
|
validRuntimes = map[string]bool{RuntimeEngine: true, RuntimeConsole: true}
|
|
validKinds = map[string]bool{KindRead: true, KindWrite: true, KindNotify: true, KindEvent: true}
|
|
)
|
|
|
|
const consoleSrc = "krow_talent_app/src/"
|
|
|
|
// SeedAgents — AI_engine's nine, then the console's two.
|
|
var SeedAgents = []models.AIAgent{
|
|
{Agentid: "JARVIS", Name: "JARVIS", Runtime: RuntimeEngine, Classref: "AI_engine/core/agent.py:196 MasterAgent",
|
|
Purpose: "Orchestrator and escalation inbox. Receives EXCEPTION_DETECTED from other agents.",
|
|
Wakeon: "NATS logistics.direct.JARVIS", Status: StatusPartial, Sortorder: 10},
|
|
{Agentid: "DISPATCH_AGENT", Name: "Dispatch", Runtime: RuntimeEngine, Classref: "AI_engine/agents/dispatch_agent.py:72",
|
|
Purpose: "Watches assignment outcomes and flags coverage gaps. Alerts; notifies customers only when autonomous.",
|
|
Wakeon: "JetStream booking.assigned, booking.assignment_failed",
|
|
Status: StatusLive, Llmdecision: "decide_assignment_failure", Hasautonomygate: true, Sortorder: 20},
|
|
{Agentid: "EXCEPTION_AGENT", Name: "Exception", Runtime: RuntimeEngine, Classref: "AI_engine/agents/exception_agent.py:106",
|
|
Purpose: "Detects stalled riders and decides the response. Reassigns only when autonomous and confident.",
|
|
Wakeon: "TRACKING miler.location.updated, miler.stalled; 60 s database sweep",
|
|
Status: StatusLive, Llmdecision: "decide_stall_response", Hasautonomygate: true, Sortorder: 30},
|
|
{Agentid: "EXPRESS_DISPATCH_AGENT", Name: "Express Dispatch", Runtime: RuntimeEngine, Classref: "AI_engine/agents/express_dispatch_agent.py:80",
|
|
Purpose: "Tenant-scoped batch assignment for DoormileExpress, then road sequencing of each rider's stops.",
|
|
Wakeon: "JetStream express.dispatch_requested", Status: StatusLive, Hasautonomygate: true, Sortorder: 40},
|
|
{Agentid: "CUSTOMER_AGENT", Name: "Customer", Runtime: RuntimeEngine, Classref: "AI_engine/agents/customer_agent.py:50",
|
|
Purpose: "Customer notifications and tracking. Reached only from an autonomous Dispatch agent.",
|
|
Wakeon: "Direct task", Status: StatusPartial, Sortorder: 50},
|
|
{Agentid: "ORDER_AGENT", Name: "Order", Runtime: RuntimeEngine, Classref: "AI_engine/agents/order_agent.py:15",
|
|
Purpose: "Order intake and validation. Not connected: it has no working backend route (/admin/* needs a console JWT, crmbooking is gone), so its backend calls are refused and logged.",
|
|
Wakeon: "Direct task (no sender in production)", Status: StatusBroken, Sortorder: 60},
|
|
{Agentid: "HUB_AGENT", Name: "Hub", Runtime: RuntimeEngine, Classref: "AI_engine/agents/hub_agent.py:35",
|
|
Purpose: "Hub capacity over 8 hard-coded fictional hubs.", Wakeon: "Direct task", Status: StatusSimulation, Sortorder: 70},
|
|
{Agentid: "FLEET_AGENT", Name: "Fleet", Runtime: RuntimeEngine, Classref: "AI_engine/agents/fleet_agent.py:34",
|
|
Purpose: "An in-memory fleet of 19 fake vehicles.", Wakeon: "Direct task", Status: StatusSimulation, Sortorder: 80},
|
|
{Agentid: "ROUTE_OPTIMIZER", Name: "Route Optimizer", Runtime: RuntimeEngine, Classref: "AI_engine/agents/route_optimizer_agent.py:41",
|
|
Purpose: "Haversine routing with traffic multipliers. Real sequencing is done by Express Dispatch via routes.workolik.com.",
|
|
Wakeon: "Direct task", Status: StatusSimulation, Sortorder: 90},
|
|
{Agentid: "CONSOLE_ASSISTANT", Name: "Console Assistant", Runtime: RuntimeConsole, Classref: consoleSrc + "lib/assistant",
|
|
Purpose: "The Home chat: orders, bulk upload, assignment, repeat runs. Regex intent catalogue; every write is a proposal the operator confirms.",
|
|
Wakeon: "Operator prompt", Status: StatusLive, Sortorder: 100},
|
|
{Agentid: "CONSOLE_OPS_AGENT", Name: "Console Ops Agent", Runtime: RuntimeConsole, Classref: consoleSrc + "lib/assistant/agent",
|
|
Purpose: "The Exceptions early-warnings banner and the chat's 'what needs attention' briefing: eight rule-based monitoring skills over the booking scan. Nothing runs on its own; an action runs only when an operator clicks it.",
|
|
Wakeon: "Exceptions page load, 60 s poll, and the chat briefing", Status: StatusLive, Sortorder: 110},
|
|
}
|
|
|
|
// obj builds a JSON-schema object for a tool's input. Only parameters the code
|
|
// provably takes are listed; where the body is not pinned down, the schema is
|
|
// left open rather than invented.
|
|
func obj(props map[string]map[string]string, required ...string) string {
|
|
properties := map[string]any{}
|
|
for name, p := range props {
|
|
properties[name] = p
|
|
}
|
|
s := map[string]any{"type": "object", "properties": properties}
|
|
if len(required) > 0 {
|
|
s["required"] = required
|
|
}
|
|
return mustJSON(s)
|
|
}
|
|
|
|
var (
|
|
integer = func(desc string) map[string]string { return map[string]string{"type": "integer", "description": desc} }
|
|
number = func(desc string) map[string]string { return map[string]string{"type": "number", "description": desc} }
|
|
str = func(desc string) map[string]string { return map[string]string{"type": "string", "description": desc} }
|
|
open = obj(map[string]map[string]string{})
|
|
)
|
|
|
|
// SeedTools — every capability a seeded skill uses, and nothing else.
|
|
var SeedTools = []models.AITool{
|
|
// AI_engine
|
|
{Toolname: "reassign_booking", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "Reassign a booking whose rider has stalled.", Target: "doormile_backend POST /internal/bookings/:id/reassign",
|
|
Implementedat: "AI_engine/agents/exception_agent.py:466", Inputschema: obj(map[string]map[string]string{"booking_id": integer("Booking to reassign")}, "booking_id")},
|
|
{Toolname: "notify_customer", Kind: KindNotify, Requiresconfirmation: true,
|
|
Description: "Send a customer a delivery update.", Target: "doormile_backend POST /internal/notify",
|
|
Implementedat: "AI_engine/agents/exception_agent.py:476, customer_agent.py:349", Inputschema: open},
|
|
{Toolname: "list_express_bookings", Kind: KindRead,
|
|
Description: "Read the bookings in an express dispatch batch.", Target: "doormile_backend GET /internal/express/bookings",
|
|
Implementedat: "AI_engine/agents/express_dispatch_agent.py:351", Inputschema: open},
|
|
{Toolname: "list_express_riders", Kind: KindRead,
|
|
Description: "Read a tenant's riders available for an express batch.", Target: "doormile_backend GET /internal/express/riders",
|
|
Implementedat: "AI_engine/agents/express_dispatch_agent.py:342", Inputschema: open},
|
|
{Toolname: "assign_express_batch", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "Write back the rider assignments decided for an express batch.", Target: "doormile_backend POST /internal/express/assign",
|
|
Implementedat: "AI_engine/agents/express_dispatch_agent.py:222", Inputschema: open},
|
|
{Toolname: "sequence_stops", Kind: KindRead,
|
|
Description: "Order a rider's stops by road (computes, writes nothing).", Target: "routes.workolik.com POST /api/v1/optimization/doormile/sequence",
|
|
Implementedat: "AI_engine/agents/express_dispatch_agent.py:308", Inputschema: open},
|
|
{Toolname: "get_booking_cache", Kind: KindRead,
|
|
Description: "Read a booking from the backend's booking cache.", Target: "doormile_backend GET /bookings/cache/:id",
|
|
Implementedat: "AI_engine/agents/customer_agent.py:243", Inputschema: obj(map[string]map[string]string{"booking_id": integer("Booking to read")}, "booking_id")},
|
|
{Toolname: "nearby_milers", Kind: KindRead,
|
|
Description: "Find riders near a point from live positions.", Target: "Redis GEO milers:locations",
|
|
Implementedat: "AI_engine/agents/dispatch_agent.py:170-240",
|
|
Inputschema: obj(map[string]map[string]string{
|
|
"lat": number("Latitude of the point, decimal degrees"), "lon": number("Longitude of the point, decimal degrees"),
|
|
"radius_km": number("Search radius in km (default 5, at most 10)"),
|
|
}, "lat", "lon")},
|
|
{Toolname: "publish_miler_stalled", Kind: KindEvent,
|
|
Description: "Publish a stalled-rider event for other agents.", Target: "NATS miler.stalled",
|
|
Implementedat: "AI_engine/agents/exception_agent.py:377", Inputschema: open},
|
|
{Toolname: "decide_stall_response", Kind: KindRead,
|
|
Description: "Ask the model how to respond to a stalled rider (structured output).", Target: "Claude via AI_engine/core/llm.py",
|
|
Implementedat: "AI_engine/core/llm.py:157", Inputschema: open},
|
|
{Toolname: "decide_assignment_failure", Kind: KindRead,
|
|
Description: "Ask the model why an assignment failed and what to do (structured output).", Target: "Claude via AI_engine/core/llm.py",
|
|
Implementedat: "AI_engine/core/llm.py:227", Inputschema: open},
|
|
|
|
// Console ops layer (krow_talent_app, ported onto main in Phase 3). The
|
|
// tool names are the proposal VERBS the findings carry, because that is what
|
|
// an operator's click resolves (lib/assistant/agent/actions.js). Only
|
|
// notify_riders has an executor; every other write is review-only and the
|
|
// console renders it disabled — the descriptions say so.
|
|
{Toolname: "scan_bookings", Kind: KindRead,
|
|
Description: "Read open and recent bookings (drained page by page) for the rules to evaluate.", Target: "doormile_backend GET /admin/bookings",
|
|
Implementedat: consoleSrc + "lib/assistant/scan.js",
|
|
Inputschema: obj(map[string]map[string]string{
|
|
"status": str("Only bookings in this status, e.g. Created, Miler_Assigned, Picked_Up, Cancelled"),
|
|
"limit": integer("How many of the newest bookings (default 20, at most 50)"),
|
|
})},
|
|
{Toolname: "notify_riders", Kind: KindNotify, Requiresconfirmation: true,
|
|
Description: "Message the riders on a finding's orders. Runs only when an operator clicks it; partial success is reported as partial.", Target: "doormile_backend POST /admin/milers/:id/notify",
|
|
Implementedat: consoleSrc + "lib/assistant/agent/actions.js", Inputschema: open},
|
|
{Toolname: "assign_riders", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Would assign a finding's orders to riders, but POST /hub/bookings/batch-assign accepts hub-staff logins only, so the console cannot run it.", Target: "doormile_backend POST /hub/bookings/batch-assign (hub staff only)",
|
|
Implementedat: consoleSrc + "lib/assistant/agent/actions.js (no executor)", Inputschema: open},
|
|
{Toolname: "enforce_otp_verification", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Flag a high-value COD order as requiring the receiver's OTP at handover. No executor.", Target: "none yet",
|
|
Implementedat: consoleSrc + "lib/assistant/skills/definitions/HighValueCodSkill.js", Inputschema: obj(map[string]map[string]string{"bookingId": integer("Booking to flag")}, "bookingId")},
|
|
{Toolname: "alert_low_battery_rider", Kind: KindNotify, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Tell a rider on a low battery to charge or report to the nearest hub. No executor.", Target: "doormile_backend POST /admin/milers/:id/notify",
|
|
Implementedat: consoleSrc + "lib/assistant/skills/definitions/RiderBatterySafetySkill.js", Inputschema: obj(map[string]map[string]string{"milerId": integer("Rider to alert")}, "milerId")},
|
|
{Toolname: "dispatch_hub_idle_parcels", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Send an idle rider to collect parcels dwelling at a hub. No executor.", Target: "none yet",
|
|
Implementedat: consoleSrc + "lib/assistant/skills/definitions/HubCongestionSkill.js",
|
|
Inputschema: obj(map[string]map[string]string{"hubId": str("Hub where parcels are waiting"), "milerId": integer("Idle rider")}, "hubId", "milerId")},
|
|
{Toolname: "trigger_auto_dispatch", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Auto-assign orders that have waited too long for dispatch. No executor.", Target: "none yet",
|
|
Implementedat: consoleSrc + "lib/assistant/skills/definitions/LateDispatchSkill.js", Inputschema: open},
|
|
{Toolname: "enforce_cash_handoff", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "REVIEW ONLY. Route a rider carrying too much COD via the nearest hub. No executor.", Target: "none yet",
|
|
Implementedat: consoleSrc + "lib/assistant/skills/definitions/CashExposureSkill.js", Inputschema: open},
|
|
|
|
// Console assistant
|
|
{Toolname: "simulate_pricing_quote", Kind: KindRead,
|
|
Description: "Quote a delivery from the tenant's pricing row and the routed distance, without booking it.", Target: "doormile_backend GET /admin/pricing + OSRM route",
|
|
Implementedat: consoleSrc + "lib/assistant/orderFlow.js", Inputschema: open},
|
|
{Toolname: "create_single_order", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "Create one express booking. Returns a proposal; the operator confirms.", Target: "doormile_backend POST /admin/expressbooking",
|
|
Implementedat: consoleSrc + "lib/assistant/orderFlow.js", Inputschema: open},
|
|
{Toolname: "rebalance_riders", Kind: KindWrite, Requiresconfirmation: true,
|
|
Description: "Move idle riders between zones. NOT IMPLEMENTED: no endpoint does zone rebalancing yet.", Target: "none yet",
|
|
Implementedat: "none", Inputschema: open}}
|
|
|
|
// SeedSkill pairs a skill row with its tool links and threshold schema.
|
|
type SeedSkill struct {
|
|
Skill models.AISkill
|
|
Tools []string
|
|
Schema []ThresholdSpec
|
|
}
|
|
|
|
func minutes(key, label string, def, min, max, step float64) ThresholdSpec {
|
|
return ThresholdSpec{Key: key, Label: label, Unit: "min", Default: def, Min: min, Max: max, Step: step}
|
|
}
|
|
|
|
// SeedSkills. The console ops skills keep the ids and threshold keys of the
|
|
// feat/agentic-ops-layer branch, which were ported onto main one to one (Phase 3).
|
|
var SeedSkills = []SeedSkill{
|
|
// AI_engine. Thresholds mirror the env knobs the agents read today; the
|
|
// engine starts reading them from here in Phase 5.
|
|
{Skill: models.AISkill{Skillid: "stall_response", Agentid: "EXCEPTION_AGENT", Title: "Stalled-rider response", Category: "rider_operations", Source: SourceEngine, Enabled: true,
|
|
Description: "Detect a rider who has stopped moving, ask the model what to do, and alert or (when autonomous) reassign."},
|
|
Tools: []string{"nearby_milers", "decide_stall_response", "reassign_booking", "notify_customer", "publish_miler_stalled"},
|
|
Schema: []ThresholdSpec{
|
|
minutes("stallMinutes", "Stall threshold", 10, 5, 60, 5),
|
|
{Key: "reassignConfidence", Label: "Auto-reassign confidence floor", Default: 0.7, Min: 0.5, Max: 1, Step: 0.05},
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "assignment_failure_triage", Agentid: "DISPATCH_AGENT", Title: "Assignment-failure triage", Category: "dispatch", Source: SourceEngine, Enabled: true,
|
|
Description: "When no rider could be assigned, work out why from nearby supply and raise one alert per gap."},
|
|
Tools: []string{"nearby_milers", "decide_assignment_failure", "notify_customer"},
|
|
Schema: []ThresholdSpec{
|
|
{Key: "realertEvery", Label: "Re-alert after N repeat failures", Unit: "failures", Default: 100, Min: 10, Max: 1000, Step: 10},
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "express_batch_dispatch", Agentid: "EXPRESS_DISPATCH_AGENT", Title: "Express batch dispatch", Category: "dispatch", Source: SourceEngine, Enabled: true,
|
|
Description: "Assign a tenant's express batch to its riders greedily, then sequence each rider's stops by road."},
|
|
Tools: []string{"list_express_bookings", "list_express_riders", "sequence_stops", "assign_express_batch"},
|
|
Schema: []ThresholdSpec{
|
|
{Key: "maxRadiusKm", Label: "Max rider radius", Unit: "km", Default: 30, Min: 5, Max: 60, Step: 1},
|
|
{Key: "maxPerRider", Label: "Max stops per rider", Unit: "stops", Default: 5, Min: 1, Max: 10, Step: 1},
|
|
{Key: "loadPenaltyKm", Label: "Load penalty per held stop", Unit: "km", Default: 3, Min: 0, Max: 10, Step: 0.5},
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "customer_notifications", Agentid: "CUSTOMER_AGENT", Title: "Customer notifications", Category: "customer", Source: SourceEngine, Enabled: true,
|
|
Description: "Tell a customer what is happening to their delivery."},
|
|
Tools: []string{"get_booking_cache", "notify_customer"}},
|
|
|
|
// Console ops layer (on main since Phase 3). Ids and threshold keys match
|
|
// lib/assistant/skills/definitions; defaults are copied from them. Every skill
|
|
// reads the booking scan; its other tools are the actions its findings propose.
|
|
{Skill: models.AISkill{Skillid: "skill_sla_guardian", Agentid: "CONSOLE_OPS_AGENT", Title: "SLA Breach Guardian", Category: "sla_management", Source: SourceConsole, Enabled: true,
|
|
Description: "Flags breached and imminent SLA violations against promised delivery ETAs."},
|
|
Tools: []string{"scan_bookings", "notify_riders", "assign_riders"},
|
|
Schema: []ThresholdSpec{
|
|
minutes("slaRiskWindowMin", "At-risk warning window", 45, 15, 90, 5),
|
|
minutes("unassignedAgingMin", "Unassigned aging threshold", 60, 15, 120, 5),
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "skill_doorstep_stall", Agentid: "CONSOLE_OPS_AGENT", Title: "Doorstep Stall Rescuer", Category: "rider_operations", Source: SourceConsole, Enabled: true,
|
|
Description: "Flags riders who marked arrival at the doorstep but have made no progress since."},
|
|
Tools: []string{"scan_bookings", "notify_riders"},
|
|
Schema: []ThresholdSpec{minutes("arrivedStalledMin", "Doorstep stall timeout", 20, 10, 60, 5)}},
|
|
{Skill: models.AISkill{Skillid: "skill_fleet_balancer", Agentid: "CONSOLE_OPS_AGENT", Title: "Fleet Load Balancer", Category: "fleet_optimization", Source: SourceConsole, Enabled: true,
|
|
Description: "Flags riders at maximum active capacity while queued work waits."},
|
|
Tools: []string{"scan_bookings"}, // flags only; its findings propose no action
|
|
Schema: []ThresholdSpec{{Key: "riderActiveCap", Label: "Rider active capacity cap", Unit: "orders", Default: 3, Min: 1, Max: 6, Step: 1}}},
|
|
{Skill: models.AISkill{Skillid: "skill_high_value_cod", Agentid: "CONSOLE_OPS_AGENT", Title: "High-Value Cash Guardian", Category: "loss_prevention", Source: SourceConsole,
|
|
Enabled: false, // no data: /admin/bookings rows carry no payment amount or mode
|
|
Description: "Audits large cash-on-delivery consignments. OFF: the booking rows it reads carry no payment amount or mode (bookingpayments is not preloaded), so enabled it would always report all-clear."},
|
|
Tools: []string{"scan_bookings", "enforce_otp_verification"},
|
|
Schema: []ThresholdSpec{{Key: "codRiskThresholdAmount", Label: "High-value COD threshold", Unit: "₹", Default: 3000, Min: 1000, Max: 20000, Step: 500}}},
|
|
{Skill: models.AISkill{Skillid: "skill_rider_battery_safety", Agentid: "CONSOLE_OPS_AGENT", Title: "Rider Device & SOS Safety", Category: "rider_safety", Source: SourceConsole,
|
|
Enabled: false, // no data: battery lives on milerprofiles, not booking rows
|
|
Description: "Warns before a rider becomes unreachable on a flat battery. OFF: battery level is on the rider profile, not on the booking rows it reads, so enabled it would always report all-clear."},
|
|
Tools: []string{"scan_bookings", "alert_low_battery_rider"},
|
|
Schema: []ThresholdSpec{{Key: "criticalBatteryPercent", Label: "Critical battery level", Unit: "%", Default: 15, Min: 5, Max: 30, Step: 5}}},
|
|
{Skill: models.AISkill{Skillid: "skill_hub_congestion", Agentid: "CONSOLE_OPS_AGENT", Title: "Hub Congestion Agent", Category: "sla_management", Source: SourceConsole, Enabled: true,
|
|
Description: "Detects parcels dwelling at a hub without rider pickup and proposes the nearest idle rider."},
|
|
Tools: []string{"scan_bookings", "dispatch_hub_idle_parcels"},
|
|
Schema: []ThresholdSpec{minutes("hubDwellMinutes", "Hub dwell threshold", 45, 15, 120, 5)}},
|
|
{Skill: models.AISkill{Skillid: "skill_late_dispatch", Agentid: "CONSOLE_OPS_AGENT", Title: "Late Dispatch Agent", Category: "sla_management", Source: SourceConsole, Enabled: true,
|
|
Description: "Flags accepted orders still waiting for dispatch and proposes auto-assignment."},
|
|
Tools: []string{"scan_bookings", "trigger_auto_dispatch"},
|
|
Schema: []ThresholdSpec{
|
|
minutes("lateDispatchMinutes", "Dispatch deadline", 30, 10, 90, 5),
|
|
minutes("criticalDispatchMinutes", "Critical dispatch deadline", 60, 30, 180, 10),
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "skill_cash_exposure", Agentid: "CONSOLE_OPS_AGENT", Title: "Cash Exposure Agent", Category: "loss_prevention", Source: SourceConsole,
|
|
Enabled: false, // no data: no COD amount per order in /admin/bookings rows
|
|
Description: "Tracks COD cash per rider and proposes a hub handoff. OFF: the booking rows it reads carry no COD amount, so enabled it would always report all-clear."},
|
|
Tools: []string{"scan_bookings", "enforce_cash_handoff"},
|
|
Schema: []ThresholdSpec{
|
|
{Key: "maxCashPerRider", Label: "Max safe cash per rider", Unit: "₹", Default: 10000, Min: 2000, Max: 50000, Step: 1000},
|
|
{Key: "warningCashPercent", Label: "Warning threshold", Unit: "%", Default: 75, Min: 50, Max: 95, Step: 5},
|
|
}},
|
|
{Skill: models.AISkill{Skillid: "ops_briefing", Agentid: "CONSOLE_OPS_AGENT", Title: "Ops briefing", Category: "operations", Source: SourceConsole, Enabled: true,
|
|
Description: "Answer \"what needs attention right now\" in the chat by running every enabled monitoring skill over the booking scan — the same engine as the Exceptions banner.", Sampleprompt: "What needs attention right now?"},
|
|
Tools: []string{"scan_bookings"}},
|
|
{Skill: models.AISkill{Skillid: "dispatch_rebalance", Agentid: "CONSOLE_OPS_AGENT", Title: "Dispatch Rebalance & Allocation", Category: "dispatch", Source: SourceConsole,
|
|
// Off: rebalance_riders has no implementation behind it.
|
|
Enabled: false,
|
|
Description: "Move idle riders into zones with a demand spike. Disabled until an endpoint implements it.",
|
|
Sampleprompt: "Rebalance available riders into Zone 1 to prevent SLA delays"},
|
|
Tools: []string{"rebalance_riders"}},
|
|
|
|
// Console assistant
|
|
{Skill: models.AISkill{Skillid: "order_intake_auto_schedule", Agentid: "CONSOLE_ASSISTANT", Title: "Order Intake & Auto-Schedule", Category: "logistics", Source: SourceConsole, Enabled: true,
|
|
Description: "Parse orders from text or a sheet, price them, and create them once the operator confirms.",
|
|
Sampleprompt: "Repeat yesterday's orders for Neptune"},
|
|
Tools: []string{"create_single_order", "simulate_pricing_quote"}},
|
|
}
|