Add introspection tools for internal agent - #543
Conversation
Persist a refusal message when input rails block background chats so Slack replaces the thinking placeholder instead of hanging. Colocate each introspection Args schema with its tool function. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR adds shared helpers for validation, timestamp formatting, truncation, metrics periods, and tool history, then updates routes and agent tools to use them. It also adds introspection tools, wires them into cloud tooling, refactors Notion postmortem action-item handling, and changes background guardrail message delivery. ChangesShared utilities, introspection tools, and agent/route refactoring
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…once' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/chat/backend/agent/tools/notion/postmortem.py (1)
250-334:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix
_create_action_itemsreturn contract to avoid runtime crashes.
_create_action_itemsreturns0on the empty-items path but returns a dict elsewhere. Both callers dereference with.get(...), so the empty path triggersAttributeErrorand fails valid requests with no unchecked action items.💡 Proposed fix
-def _create_action_items( +def _create_action_items( client: NotionClient, postmortem_md: str, action_items_database_id: str, -) -> int: +) -> Dict[str, int]: """Create one Notion page per unchecked action item in the target DB. - Returns the number of pages successfully created. + Returns counts of created/failed pages. """ items = parse_action_items(postmortem_md or "") if not items: - return 0 + return {"created": 0, "failed": 0} @@ - return {"created": created, "failed": failed} + return {"created": created, "failed": failed}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/chat/backend/agent/tools/notion/postmortem.py` around lines 250 - 334, The _create_action_items function has an inconsistent return type that causes AttributeError in callers. When items is empty, the function returns the integer 0, but at the end of the function it returns a dictionary with "created" and "failed" keys. Since callers invoke .get() on the return value expecting a dictionary, the early return path fails. Change the early return statement when items is empty to return a dictionary {"created": 0, "failed": 0} instead of returning 0 to maintain consistency with the final return statement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/chat/backend/agent/tools/cloud_tools.py`:
- Around line 1324-1327: The tool registration logic at line 1326 exposes
trigger_rca to background sessions via the is_background condition, but the
trigger_rca function itself returns an error whenever state.is_background is
true, creating a contradiction. Either remove the is_background condition from
the tool registration check to keep trigger_rca UI-gated only, or modify the
guard logic inside the trigger_rca function itself to be more specific and only
block background RCA recursion rather than all background executions, ensuring
the tool behavior aligns with when it is registered.
- Around line 1821-1841: The imports in the introspection_tools section are
using names that conflict with incident.io tool schemas, specifically
ListIncidentsArgs and GetIncidentArgs. To fix this, alias the introspection tool
imports using the `as` keyword to give them distinct names that won't overwrite
the incident.io schemas (for example, prefix them with "Introspection" such as
`ListIncidentsArgs as IntrospectionListIncidentsArgs` and `GetIncidentArgs as
IntrospectionGetIncidentArgs`). Apply this aliasing consistently to all imported
Args classes from introspection_tools to ensure the incident.io tools receive
their correct schemas rather than the introspection ones.
- Around line 1329-1331: The condition on line 1331 uses `if is_background or
_action_id:` which registers trigger_action for all background sessions
regardless of whether _action_id exists, causing the wrapper to pin a None value
and ignore the caller's supplied action_id. Fix this by requiring _action_id to
actually exist before registering trigger_action, either by changing the
condition to only check `if _action_id:` (registering only when an action id is
explicitly provided), or to `if is_background and _action_id:` (registering only
for background sessions with a pinned action id), depending on the intended
behavior for unpinned background sessions.
In `@server/chat/backend/agent/tools/introspection_tools.py`:
- Around line 101-116: The list_incidents function always excludes merged
incidents in the WHERE clause regardless of user input, which prevents the
advertised status="merged" filter from working. Modify the WHERE clause
construction in list_incidents to conditionally include the i.status != 'merged'
exclusion only when the status parameter is None (i.e., when no explicit status
filter is provided). When a user provides an explicit status value, omit that
exclusion so merged incidents can be returned if that's what was requested.
In `@server/chat/background/task.py`:
- Around line 1980-1983: The code iterates through the messages list and calls
.get() on each msg element without verifying it is a dictionary first. If a
legacy or corrupt row contains a non-dict item in the messages list, this will
raise an AttributeError and abort execution. Guard both the message iteration
loop starting at line 1980 (where msg.get('sender') and
msg.get('text')/msg.get('content') are called) and the similar iteration at
lines 2113-2116 by adding an isinstance check to ensure each msg is a dict
before attempting to call .get() on it. Only process the message if it passes
the dict type check.
---
Outside diff comments:
In `@server/chat/backend/agent/tools/notion/postmortem.py`:
- Around line 250-334: The _create_action_items function has an inconsistent
return type that causes AttributeError in callers. When items is empty, the
function returns the integer 0, but at the end of the function it returns a
dictionary with "created" and "failed" keys. Since callers invoke .get() on the
return value expecting a dictionary, the early return path fails. Change the
early return statement when items is empty to return a dictionary {"created": 0,
"failed": 0} instead of returning 0 to maintain consistency with the final
return statement.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e9a50961-58e5-4675-92cd-2896cab92cce
📒 Files selected for processing (20)
server/chat/backend/agent/orchestrator/sub_agent.pyserver/chat/backend/agent/tools/cloud_tools.pyserver/chat/backend/agent/tools/introspection_tools.pyserver/chat/backend/agent/tools/notion/postmortem.pyserver/chat/backend/agent/tools/postmortem_tool.pyserver/chat/backend/agent/utils/tool_call_history.pyserver/chat/backend/agent/utils/tool_context_capture.pyserver/chat/background/task.pyserver/routes/actions/actions_routes.pyserver/routes/artifact_routes.pyserver/routes/audit_routes.pyserver/routes/incident_feedback/routes.pyserver/routes/incidents_findings.pyserver/routes/incidents_routes.pyserver/routes/metrics_routes.pyserver/routes/postmortem_routes.pyserver/utils/metrics_periods.pyserver/utils/query_helpers.pyserver/utils/text/text_utils.pyserver/utils/validation.py
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/chat/backend/agent/tools/notion/postmortem.py (1)
59-89:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHarden coercion for
date/url/checkboxto avoid invalid or incorrect Notion writes.
_coerce_property_valuecurrently accepts arbitrary strings for date/email/url and usesbool(value)for checkbox, which can silently convert"false"toTrue. This can either fail downstream at Notion API time or persist incorrect values.Suggested fix
def _coerce_property_value(prop_meta: Dict[str, Any], value: Any) -> Optional[Dict[str, Any]]: @@ - if prop_type == "date": - iso = value if isinstance(value, str) else str(value) - return {"date": {"start": iso}} + if prop_type == "date": + if hasattr(value, "isoformat"): + iso = value.isoformat() + elif isinstance(value, str): + iso = value.strip() + if not iso: + return None + else: + return None + return {"date": {"start": iso}} @@ - if prop_type == "checkbox": - return {"checkbox": bool(value)} + if prop_type == "checkbox": + if isinstance(value, bool): + return {"checkbox": value} + if isinstance(value, str): + normalized = value.strip().lower() + if normalized in {"true", "1", "yes", "y"}: + return {"checkbox": True} + if normalized in {"false", "0", "no", "n"}: + return {"checkbox": False} + return None + if isinstance(value, (int, float)): + return {"checkbox": bool(value)} + return None @@ - if prop_type == "url": - return {"url": str(value)} - if prop_type == "email": - return {"email": str(value)} + if prop_type == "url": + url = str(value).strip() + if not url: + return None + return {"url": url} + if prop_type == "email": + email = str(value).strip() + if not _looks_like_email(email): + return None + return {"email": email}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/chat/backend/agent/tools/notion/postmortem.py` around lines 59 - 89, The _coerce_property_value function needs to add proper validation for date, email, url, and checkbox property types. For the date property type, validate that the ISO string is in a valid date format. For email and url property types, add validation to ensure the values match expected email and URL formats rather than accepting arbitrary strings. For the checkbox property type, replace the simple bool(value) call with logic that correctly handles string representations of booleans (such as "false" which should evaluate to False, not True as bool() would return).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/chat/backend/agent/tools/cloud_tools.py`:
- Around line 1329-1332: The tool-cache key generation at lines 1034 and 1036
does not include the trigger_action_id, but the tool registration logic at line
1331 uses _action_id to conditionally add the trigger_action tool. This mismatch
causes cached tools from one action to be reused for a different action, pinning
trigger_action to a stale action id. Include the trigger_action_id value
(obtained from state_context if available, similar to how _action_id is
obtained) as part of the cache key computation at both cache key generation
locations to ensure separate cache entries for different action IDs.
---
Outside diff comments:
In `@server/chat/backend/agent/tools/notion/postmortem.py`:
- Around line 59-89: The _coerce_property_value function needs to add proper
validation for date, email, url, and checkbox property types. For the date
property type, validate that the ISO string is in a valid date format. For email
and url property types, add validation to ensure the values match expected email
and URL formats rather than accepting arbitrary strings. For the checkbox
property type, replace the simple bool(value) call with logic that correctly
handles string representations of booleans (such as "false" which should
evaluate to False, not True as bool() would return).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0ee2ae8c-ee3f-4c39-ae95-902a3a53447d
📒 Files selected for processing (5)
server/chat/backend/agent/tools/cloud_tools.pyserver/chat/backend/agent/tools/introspection_tools.pyserver/chat/backend/agent/tools/notion/postmortem.pyserver/chat/backend/agent/tools/trigger_rca_tool.pyserver/chat/background/task.py
Persist a refusal message when input rails block background chats so Slack replaces the thinking placeholder instead of hanging. Colocate each introspection Args schema with its tool function. Co-authored-by: Cursor <cursoragent@cursor.com>
…once' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
711bf64 to
4fefbeb
Compare
…key conflicts Keep bool return from early Slack send, fallback_text for guardrail blocks, and stable capture_tag cache keys with action_id differentiation. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Aurora Risk Review
Verdict: RISKY
The new introspection tools are well-structured and the Slack guardrail fix is clean. However, two queries in introspection_tools.py — list_action_runs and get_action — omit an explicit org_id filter, relying solely on RLS to enforce tenant isolation. Every other query in the same file adds AND org_id = %s as defense-in-depth; these two do not, creating a cross-tenant data leak risk if action_runs or actions lack row-level security on the pooled connection type used here.
Findings
| # | Severity | File | Finding |
|---|---|---|---|
| 1 | HIGH | server/chat/backend/agent/tools/introspection_tools.py:310 |
list_action_runs query has no org_id filter — cross-tenant run history exposure |
| 2 | HIGH | server/chat/backend/agent/tools/introspection_tools.py:340 |
get_action query has no org_id filter — cross-tenant action config exposure |
Aurora reviews PRs for incident prevention. This is advisory only and does not block merge.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/chat/background/task.py`:
- Around line 1474-1479: Move the early Slack response path in
background/task.py so guardrail block handling happens before calling
_send_response_to_slack in the trigger_metadata/slack branch. Make sure the
logic around _append_block_message and the later post-processing path cannot be
skipped for blocked sessions, and pass fallback_text through the early send path
if needed so the placeholder message is updated even when
_send_response_to_slack returns False. Update the related slack send flow used
in the later block as well so both paths behave consistently.
- Around line 488-499: The dedup lock in the task execution path is being
removed on every exit, which allows an acks_late redelivery to repeat side
effects after a successful run. Update the dedup handling around the background
chat task logic in task.py so the key created with celery_app.backend.client.set
stays in Redis after successful completion and is only released safely on
failure using an owner token and compare-delete if cleanup is needed. Keep the
existing dedup flow tied to self.request.id, the _dedup_key/_dedup_acquired
flags, and the duplicate-execution skip path, but avoid unconditional deletion
in the finally block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 60954936-e5df-4bfd-b9e6-f893628bde5e
📒 Files selected for processing (4)
server/chat/backend/agent/tools/cloud_tools.pyserver/chat/backend/agent/tools/introspection_tools.pyserver/chat/backend/agent/tools/trigger_rca_tool.pyserver/chat/background/task.py
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/chat/background/task.py`:
- Around line 1474-1479: Move the early Slack response path in
background/task.py so guardrail block handling happens before calling
_send_response_to_slack in the trigger_metadata/slack branch. Make sure the
logic around _append_block_message and the later post-processing path cannot be
skipped for blocked sessions, and pass fallback_text through the early send path
if needed so the placeholder message is updated even when
_send_response_to_slack returns False. Update the related slack send flow used
in the later block as well so both paths behave consistently.
- Around line 488-499: The dedup lock in the task execution path is being
removed on every exit, which allows an acks_late redelivery to repeat side
effects after a successful run. Update the dedup handling around the background
chat task logic in task.py so the key created with celery_app.backend.client.set
stays in Redis after successful completion and is only released safely on
failure using an owner token and compare-delete if cleanup is needed. Keep the
existing dedup flow tied to self.request.id, the _dedup_key/_dedup_acquired
flags, and the duplicate-execution skip path, but avoid unconditional deletion
in the finally block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 60954936-e5df-4bfd-b9e6-f893628bde5e
📒 Files selected for processing (4)
server/chat/backend/agent/tools/cloud_tools.pyserver/chat/backend/agent/tools/introspection_tools.pyserver/chat/backend/agent/tools/trigger_rca_tool.pyserver/chat/background/task.py
🛑 Comments failed to post (2)
server/chat/background/task.py (2)
488-499: 🗄️ Data Integrity & Integration | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash python - <<'PY' from pathlib import Path p = Path("server/chat/background/task.py") s = p.read_text() finally_block = s[s.find("finally:"):s.find("# Safety net")] print("owner token present:", "_dedup_token" in s) print("atomic compare-delete present:", ".eval(" in finally_block and "redis.call('get'" in finally_block) print("success is not deleted:", "not completed_successfully" in finally_block) PYRepository: Arvo-AI/aurora
Length of output: 246
🏁 Script executed:
#!/bin/bash set -euo pipefail python3 - <<'PY' from pathlib import Path p = Path("server/chat/background/task.py") s = p.read_text().splitlines() for start, end in [(480, 510), (930, 950)]: print(f"\n--- {start}-{end} ---") for i in range(start-1, min(end, len(s))): print(f"{i+1}: {s[i]}") PYRepository: Arvo-AI/aurora
Length of output: 2792
Keep the dedup marker through successful completion.
finallydeletescelery:dedup:{self.request.id}on every exit, so anacks_lateredelivery after a crash or lost ack can run the same side effects again. Keep successful markers until TTL expiry, and use an owner token/compare-delete if cleanup must happen on failure.🧰 Tools
🪛 Ruff (0.15.18)
[warning] 497-497: Logging statement uses f-string
(G004)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/chat/background/task.py` around lines 488 - 499, The dedup lock in the task execution path is being removed on every exit, which allows an acks_late redelivery to repeat side effects after a successful run. Update the dedup handling around the background chat task logic in task.py so the key created with celery_app.backend.client.set stays in Redis after successful completion and is only released safely on failure using an owner token and compare-delete if cleanup is needed. Keep the existing dedup flow tied to self.request.id, the _dedup_key/_dedup_acquired flags, and the duplicate-execution skip path, but avoid unconditional deletion in the finally block.
1474-1479: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Handle guardrail blocks before the early Slack send.
The early Slack send runs before
_append_block_message(), and it does not passfallback_text; blocked sessions can returnFalsewith no placeholder update if the outer post-processing path does not run.Suggested fix
+ guardrail_blocked = getattr(state, "guardrail_blocked", False) + if guardrail_blocked: + source = (trigger_metadata or {}).get("source", "") + block_text = ( + _ACTION_GUARDRAIL_USER_MSG + if source == "action" + else _GUARDRAIL_USER_MSG + ) + _append_block_message(session_id, user_id, block_text) + # Send Slack response immediately — don't wait for post-processing _slack_early_sent = False if trigger_metadata and trigger_metadata.get('source') in ['slack', 'slack_button']: try: - _slack_early_sent = _send_response_to_slack(user_id, session_id, trigger_metadata) + _slack_early_sent = _send_response_to_slack( + user_id, + session_id, + trigger_metadata, + fallback_text=_GUARDRAIL_USER_MSG if guardrail_blocked else None, + ) except Exception: logger.exception("[BackgroundChat] Failed early Slack send") @@ - guardrail_blocked = getattr(state, "guardrail_blocked", False) - if guardrail_blocked: - # Streaming copies are stripped at end of workflow; persist a real bot - # message so Slack/Google Chat can replace the "Thinking..." placeholder. - source = (trigger_metadata or {}).get("source", "") - block_text = ( - _ACTION_GUARDRAIL_USER_MSG - if source == "action" - else _GUARDRAIL_USER_MSG - ) - _append_block_message(session_id, user_id, block_text)Also applies to: 1553-1564
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/chat/background/task.py` around lines 1474 - 1479, Move the early Slack response path in background/task.py so guardrail block handling happens before calling _send_response_to_slack in the trigger_metadata/slack branch. Make sure the logic around _append_block_message and the later post-processing path cannot be skipped for blocked sessions, and pass fallback_text through the early send path if needed so the placeholder message is updated even when _send_response_to_slack returns False. Update the related slack send flow used in the later block as well so both paths behave consistently.
Resolve conflict in cloud_tools.py: keep main's cleaner _is_background_rca call using the existing local is_background var. Also add org_id filters to get_action and list_action_runs to fix cross-tenant data exposure (review comments). Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/chat/backend/agent/tools/trigger_rca_tool.py (1)
75-84: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUse the RCA predicate instead of
incident_idalone.This guard says it blocks nested RCA only, but
is_background && incident_idalso blocks non-RCA background sessions that happen to be incident-scoped. Reuse the same_is_background_rca(...)predicate used incloud_tools.pyso Actions/prediscovery-style background runs are not misclassified.🐛 Proposed fix
- from chat.backend.agent.tools.cloud_tools import get_state_context + from chat.backend.agent.tools.cloud_tools import get_state_context, _is_background_rca state = get_state_context() # Block nested RCA only — background Slack/Celery sessions without an # active incident investigation may still trigger a new RCA. - if ( - state - and getattr(state, "is_background", False) - and getattr(state, "incident_id", None) - ): + if _is_background_rca(state, bool(getattr(state, "is_background", False))): return json.dumps({ "error": "Cannot trigger RCA from within a background RCA session. " "Nested RCA is not supported — finish the current investigation first." })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/chat/backend/agent/tools/trigger_rca_tool.py` around lines 75 - 84, The nested-RCA guard in trigger_rca_tool.py is using is_background plus incident_id, which can wrongly block non-RCA background sessions. Update the check in the RCA trigger path to reuse the same _is_background_rca(...) predicate used in cloud_tools.py, and keep the existing error response only for true background RCA sessions so Actions/prediscovery-style runs are not misclassified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@server/chat/backend/agent/tools/trigger_rca_tool.py`:
- Around line 75-84: The nested-RCA guard in trigger_rca_tool.py is using
is_background plus incident_id, which can wrongly block non-RCA background
sessions. Update the check in the RCA trigger path to reuse the same
_is_background_rca(...) predicate used in cloud_tools.py, and keep the existing
error response only for true background RCA sessions so
Actions/prediscovery-style runs are not misclassified.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 99a91df3-e5bb-4737-bd79-45a4eca84a35
📒 Files selected for processing (3)
server/chat/backend/agent/tools/cloud_tools.pyserver/chat/backend/agent/tools/introspection_tools.pyserver/chat/backend/agent/tools/trigger_rca_tool.py
|



Summary
Gives the internal agent (Slack, web chat, RCA) direct read access to incidents, services, actions, metrics, and findings — same data MCP clients get, without HTTP round-trips. Also fixes Slack leaving a stuck "Thinking..." message when the input guardrail blocks a request.
Test plan
Made with Cursor
Summary by CodeRabbit