{"id":50927847,"url":"https://github.com/manojmallick/incidentiq","last_synced_at":"2026-06-17T01:03:12.878Z","repository":{"id":363922285,"uuid":"1263131619","full_name":"manojmallick/incidentiq","owner":"manojmallick","description":"DORA major-incident triage agent — searches Elastic precedents, classifies against DORA Art.18 (incl. the recurring-incident rule), drafts the regulator submission with Gemini 3, and acts only after human approval. Gemini + Google Cloud Agent Builder + Elastic MCP, all at runtime.","archived":false,"fork":false,"pushed_at":"2026-06-10T22:52:45.000Z","size":547,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T00:15:37.664Z","etag":null,"topics":["agent-builder","ai-agent","cloud-run","compliance","dora","elastic","elasticsearch","gemini","google-cloud","hackathon","mcp","model-context-protocol","regtech","vertex-ai"],"latest_commit_sha":null,"homepage":"https://incidentiq-908307939543.europe-west1.run.app","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manojmallick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-08T16:47:58.000Z","updated_at":"2026-06-10T22:52:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/manojmallick/incidentiq","commit_stats":null,"previous_names":["manojmallick/incidentiq"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/manojmallick/incidentiq","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manojmallick%2Fincidentiq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manojmallick%2Fincidentiq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manojmallick%2Fincidentiq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manojmallick%2Fincidentiq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manojmallick","download_url":"https://codeload.github.com/manojmallick/incidentiq/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manojmallick%2Fincidentiq/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34429497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-builder","ai-agent","cloud-run","compliance","dora","elastic","elasticsearch","gemini","google-cloud","hackathon","mcp","model-context-protocol","regtech","vertex-ai"],"created_at":"2026-06-17T01:03:11.367Z","updated_at":"2026-06-17T01:03:12.849Z","avatar_url":"https://github.com/manojmallick.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IncidentIQ — DORA Major-Incident Triage Agent\n\n\u003cp\u003e\n\u003ca href=\"https://incidentiq-908307939543.europe-west1.run.app\"\u003e\u003cimg alt=\"Live demo\" src=\"https://img.shields.io/badge/live%20demo-online-00BFB3?style=flat-square\u0026logo=googlecloud\u0026logoColor=white\"\u003e\u003c/a\u003e\n\u003cimg alt=\"Gemini 3\" src=\"https://img.shields.io/badge/Gemini%203-gemini--3--flash--preview-4285F4?style=flat-square\u0026logo=google\u0026logoColor=white\"\u003e\n\u003cimg alt=\"Google Cloud Agent Builder\" src=\"https://img.shields.io/badge/Google%20Cloud-Agent%20Builder%20%C2%B7%20Vertex%20AI%20Agent%20Engine-4285F4?style=flat-square\u0026logo=googlecloud\u0026logoColor=white\"\u003e\n\u003cimg alt=\"Elastic MCP\" src=\"https://img.shields.io/badge/Elastic-MCP%20server-00BFB3?style=flat-square\u0026logo=elastic\u0026logoColor=white\"\u003e\n\u003cimg alt=\"Cloud Run\" src=\"https://img.shields.io/badge/Cloud%20Run-deployed-4285F4?style=flat-square\u0026logo=googlecloud\u0026logoColor=white\"\u003e\n\u003cimg alt=\"Node\" src=\"https://img.shields.io/badge/Node-20-339933?style=flat-square\u0026logo=nodedotjs\u0026logoColor=white\"\u003e\n\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/license-MIT-blue?style=flat-square\"\u003e\n\u003c/p\u003e\n\n\u003e **Google Cloud Rapid Agent Hackathon — Elastic track.** Gemini + Google Cloud Agent Builder + the Elastic MCP server, all invoked **at runtime** (no competing AI/cloud).\n\n📐 [Architecture](ARCHITECTURE.md) · 🎨 [Design system](DESIGN_SYSTEM.md) · 🪧 [Pitch deck](DECK.md) ([PDF](DECK.pdf)) · 🖼️ [Diagram](architecture.png) · 📸 [Gallery](SCREENSHOTS.md) · 🎬 [Demo video plan](DEMO_VIDEO.md)\n\n![A harmless 85% warning classified MAJOR via the DORA recurrence rule](screenshots/02-recurrence-major.png)\n\nWhen a new ICT incident hits a financial entity, IncidentIQ **reasons, plans, and acts**:\nit searches similar past incidents in Elasticsearch (hybrid kNN + keyword), classifies the\nincident against **DORA Art.18** thresholds informed by precedent and the recurring-incident\nrule, drafts the **actual DNB early-warning submission** with Gemini 3, builds a\nregulator-defensible record — and, **only after a human approves**, writes the classification,\nsaves the reporting obligations, and logs the audit trail. ~Seconds vs ~2 hours of manual work.\n\n\u003e **It's an agent, not a chatbot:** multi-step plan → real tool actions (Elastic writes) →\n\u003e human-in-the-loop approval gate → audit trail.\n\n## 🔴 Live demo\n- **App (Google Cloud Run) — open, no login:** https://incidentiq-908307939543.europe-west1.run.app\n- Click **Judge Tour** in the top bar for a guided, end-to-end walkthrough.\n- **30-second \"wow\":** on the dashboard, click the *\"DB warning: connection pool at 85% saturation\"* card — a zero-impact warning. IncidentIQ returns **MAJOR**, because it's the 6th PaymentProcessing incident in 30 days (the DORA recurring-incident rule most tools miss).\n\n## Stack (Google Cloud Rapid Agent Hackathon — Elastic bucket)\n- 🧠 **Gemini 3** (`gemini-3-flash-preview`) — classification rationale + drafts the DNB submission (real `@google/genai` calls, [`src/agent.js`](src/agent.js))\n- 🏗️ **Google Cloud Agent Builder** — the ADK agent in [`agent-builder/incidentiq_agent/`](agent-builder/incidentiq_agent/) (Gemini 3 + Elastic MCP toolset) deploys to **Vertex AI Agent Engine**; when `AGENT_ENGINE_ID` is set the app invokes it at runtime via `reasoningEngines:streamQuery` ([`src/agent-engine.js`](src/agent-engine.js)) for the explain + DNB-draft step *(verifiable in `/health` as `agent_builder_connected`)*\n- 🔍 **Elastic MCP server** (`@elastic/mcp-server-elasticsearch`) — **spawned over stdio at runtime** ([`src/elastic-mcp.js`](src/elastic-mcp.js)); hybrid kNN + keyword precedent search runs through its `search` tool *(load-bearing — verifiable in `/health` as `partner_mcp_connected`)*\n- 🔢 **`gemini-embedding-001`** (768-dim) — query + corpus vectors for kNN\n- ↪️ The MCP server v0.3.x is **read-only** (no ES|QL/write tool), so ES|QL aggregation and the human-approved index writes use the Elasticsearch REST API ([`src/elastic.js`](src/elastic.js)) — which also backs search as a fallback if the MCP child can't start.\n\n## Architecture\n```\nBrowser (public/index.html) ──POST /api/classify──► agent (src/agent.js)\n  1. embed incident            → gemini-embedding-001 (768d)\n  2. hybrid precedent search    → Elastic MCP server `search` tool (kNN + keyword), REST fallback\n  3. classify + recurrence      → criteria.js (DORA Art.18 thresholds + aggregate rule)\n  4. explain + draft submission → Gemini 3 (cites precedents; deterministic fallback)\n  5. defensibility + deadlines  → version-stamped record, Art.19 timeline\n  6. PROPOSE store + obligations ─┐  (consequential → GATED)\nApprovalBar (human approves) ──POST /api/execute──► Elastic writes + obligations ledger + audit log\n```\nThe **judged** agent is [`agent-builder/agent.json`](agent-builder/agent.json) (Gemini 3 + Elastic\nMCP, writes require approval). The hosted Express app invokes the **same Elastic MCP server at runtime**\nfor precedent search ([`src/elastic-mcp.js`](src/elastic-mcp.js)) and uses the Elastic REST API for ES|QL\naggregation + the human-approved writes (tools the read-only MCP server doesn't expose).\n\n## Two agents\n- **ElasticSearcher** — embeds the incident, runs hybrid kNN + keyword precedent search, aggregates impact via ES|QL.\n- **DORAAnalyst** — applies DORA Art.18 thresholds + the recurring-incident rule + precedent signal, then Gemini 3 explains and drafts the DNB (Art.19) submission.\n\n## DORA features\n- **Art.18 classification** — 5 major-incident thresholds (clients %, transaction value, data breach, core-banking downtime, payments downtime).\n- **Recurring-incident aggregation** — individually-MINOR incidents that recur on a service within 30 days escalate to MAJOR in aggregate (the rule most tools miss).\n- **Art.19 reporting** — early-warning (4h) / intermediate (72h) / final (1mo) deadlines with a live countdown.\n- **DNB submission draft** — the actual EBA/DORA-template notification, not a summary.\n- **Defensibility record** — version-stamped rationale a regulator can challenge.\n- **Shared obligation ledger** — same schema across the DORA platform; CSV-exportable.\n- **Cross-app handoff** — `/api/ingest` accepts a real-time detection (e.g. from DynaCompliance) and classifies + files it.\n\n## Endpoints\n| Route | Purpose |\n|---|---|\n| `GET /health` | proves the stack is wired (`model`, `partner_mcp_connected`, `indexed`) |\n| `GET /api/incidents` | open incidents via ES|QL |\n| `POST /api/classify` | steps 1–5 (read-only): search → classify → draft |\n| `POST /api/agent` | runs the full deployed Agent Builder agent (Gemini 3 + Elastic MCP) for a message |\n| `POST /api/ingest` | cross-app detection handoff → classify |\n| `POST /api/execute` | step 6 (gated on `approved:true`): writes + obligations + audit |\n| `GET /api/obligations/:companyId` | shared ledger (`?format=csv` to export) |\n\n## Quick start\n\n**Zero-credential demo (canned data):**\n```bash\nnpm install\nMOCK=true npm start          # → http://localhost:8080\n```\n\n**Live (real Gemini 3 + Elastic):**\n```bash\ncp .env.example .env         # fill GEMINI_API_KEY + ELASTIC_URL + ELASTIC_API_KEY\nnpm install\nnpm run setup:elastic        # creates ict-incidents (768d vectors) + seeds 128 incidents (--reset to recreate)\nnpm start                    # → http://localhost:8080\n```\n\n### Environment (`.env`)\n| Var | Notes |\n|---|---|\n| `GEMINI_API_KEY` | Gemini Developer API key (AI Studio). Enables `gemini-3-flash-preview` + embeddings on any host. |\n| `GEMINI_MODEL` | `gemini-3-flash-preview` |\n| `EMBEDDING_MODEL` | `gemini-embedding-001` (768-dim) |\n| `ELASTIC_URL` / `ELASTIC_API_KEY` | Elastic Cloud endpoint + a scoped Elasticsearch API key |\n| `ELASTIC_*_INDEX` | `ict-incidents` / `obligations` / `audit_log` |\n| `APP_PASSWORD` (+ `APP_USERNAME`) | optional HTTP basic-auth gate (set on public deploys) |\n| `MOCK=true` | run the full UI with canned data, no credentials |\n\n## Deploy\n- **Google Cloud Run (primary):** `gcloud run deploy incidentiq --source . --region=europe-west1 --allow-unauthenticated --set-env-vars=\"GEMINI_MODEL=gemini-3-flash-preview,...\"` (Dockerfile included). Cloud Run keeps the container warm, so the Elastic MCP child process is spawned once and reused.\n- The MCP server runs as a child process and needs to stay alive across requests, so a long-lived host (Cloud Run) is the supported target rather than per-request serverless.\n\n### Deploy the Agent Builder agent (Vertex AI Agent Engine)\nThe [`agent-builder/incidentiq_agent/`](agent-builder/incidentiq_agent/) ADK agent (Gemini 3 + Elastic MCP toolset) is the runtime form of [`agent.json`](agent-builder/agent.json):\n```bash\ncd agent-builder\npip install -r requirements.txt\ngcloud auth application-default login\nexport GOOGLE_CLOUD_PROJECT=... GOOGLE_CLOUD_LOCATION=us-central1 STAGING_BUCKET=gs://your-bucket\nexport ELASTIC_URL=... ELASTIC_API_KEY=...\npython deploy.py            # prints AGENT_ENGINE_ID=projects/.../reasoningEngines/123\n```\nSet the printed `AGENT_ENGINE_ID` on the Node app. Then `/api/classify` routes its explain + DNB-draft through the deployed agent, and `POST /api/agent {\"message\": \"...\"}` runs the full agent end-to-end for judges. `/health` shows `agent_builder_connected: true`.\n\n\u003e **Runtime caveat:** the Elastic MCP server is a Node package (`npx`). The managed Agent Engine (Python) runtime has no Node, so by default the deployed ADK agent runs tool-free and receives the MCP-fetched precedents in its prompt (the Node app does the Elastic MCP search). To give the deployed agent its own Elastic MCP tool, deploy with `adk deploy cloud_run` (a container that includes Node) and set `ELASTIC_MCP_IN_AGENT=true`. See [`agent-builder/deploy.py`](agent-builder/deploy.py).\n\n\u003e **Model note:** the deployed Agent Engine agent runs **`gemini-2.5-flash`** (a Vertex-GA Gemini), because `gemini-3-flash-preview` is served by the Gemini Developer API, not as a Vertex regional publisher model. The app's direct path still uses `gemini-3-flash-preview`. Both are Gemini; the split is purely about where each model is available.\n\n## Health check (proof for judges)\n```bash\ncurl https://incidentiq-908307939543.europe-west1.run.app/health\n# { \"status\":\"ok\", \"mode\":\"live\", \"model\":\"gemini-3-flash-preview\", \"partner\":\"elastic\",\n#   \"partner_mcp_connected\":true,     # ← true only when the Elastic MCP child is connected + search routes through it\n#   \"agent_builder_connected\":true,   # ← true only when the Vertex AI Agent Engine deployment is reachable\n#   \"elastic_rest_connected\":true, \"indexed\":128, ... }\n```\n\n## Evals\n`npm run eval` runs the golden classification set in [`evals/`](evals/).\n\n## Learnings\n- **An MCP server's stdout is sacred.** The Elastic MCP server bundles EDOT\n  (`@elastic/opentelemetry-node`), whose bootstrap banner prints to **stdout** — the same\n  channel the JSON-RPC protocol uses — silently corrupting every message. Setting\n  `OTEL_SDK_DISABLED=true` in the child env was the difference between \"connected\" and a\n  stream of Zod validation errors.\n- **Read the partner server before trusting the docs.** The Elastic MCP server v0.3.x is\n  **read-only** (`search`, `list_indices`, `get_mappings`, `get_shards`) — no ES|QL or write\n  tool. We split the work honestly: search through MCP, ES|QL + gated writes through REST,\n  rather than claiming tools that don't exist.\n- **Design for graceful degradation.** Every external hop (Gemini, embeddings, the MCP child)\n  has a fallback so a single failure degrades a feature instead of taking down the agent — and\n  `/health` reports what's *actually* connected, not what we wish were.\n- **DORA's hardest rule is the one tools skip:** individually-minor incidents that recur on a\n  service aggregate to a *major* incident. Encoding that (ES|QL `STATS` over a 30-day window)\n  was where the domain depth lived.\n\n## License\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanojmallick%2Fincidentiq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanojmallick%2Fincidentiq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanojmallick%2Fincidentiq/lists"}