Skip to content

feat(inventory): publish introspected tool/permission inventory and honest cloud tiers - #66

Merged
AhmadHammad21 merged 4 commits into
mainfrom
fm/odo-inv-x4
Jun 25, 2026
Merged

feat(inventory): publish introspected tool/permission inventory and honest cloud tiers#66
AhmadHammad21 merged 4 commits into
mainfrom
fm/odo-inv-x4

Conversation

@AhmadHammad21

Copy link
Copy Markdown
Owner

Intent

Ship Phase 0 of round-2 feedback: a published tool/permission inventory (trust artifact) and honest multi-cloud positioning, with zero agent-behavior change.

Part A — inventory, sourced by INTROSPECTION so it never drifts (no hand-maintained list): new core module opendevops_core/agent/inventory.py (build_inventory) introspects ALL_TOOLS (name, docstring, type-hinted params), the bash command allowlist, the AWS read-permission matrix, and per-provider capability tiers. Exposed via a read-only GET /api/inventory endpoint (deliberately uses the /api/ prefix to avoid the SPA-fallback catch-all) and a GENERATED doc page apps/documentation/tool_inventory.md produced by scripts/gen_tool_inventory.py (header marks it do-not-edit-by-hand). To make the sources introspectable WITHOUT changing behavior, two deliberate behavior-preserving refactors: (1) promoted the kubectl/docker allowlist literals in bash_tool.py to module constants _KUBECTL_SUBCOMMANDS/_DOCKER_SUBCOMMANDS (identical values), and (2) rewrote permissions.check_permissions() to iterate a declarative PERMISSION_PROBES table (identical returned dict shape and identical boto3 calls). These two refactors are intentional and must not be flagged as accidental scope creep.

Part B — honest positioning in README + the doc page: AWS = complete (structured tools + CLI), Azure = CLI + 4 runbook skills with NO structured SDK tools and NO event-driven/polling loop, GCP = not implemented (stub returns no tools). Verified against providers/azure and providers/gcp. I deliberately corrected stale tool counts: the introspected reality is 26 total tools / 20 structured AWS tools (CloudWatch 6, CloudTrail 1, ECS 4, Lambda 3, EC2 2, RDS 2, IAM 2). The pre-existing README/CLAUDE.md '27/21' figures were wrong; the new numbers are intentional, not a regression.

Tests: added test_api/test_inventory.py (asserts the endpoint reflects ALL_TOOLS, the allowlist constants, the permission probes, and the honest provider tiers) and test_tools/test_permissions.py (locks the behavior-preserving probe refactor). Note: the repo has ~42 PRE-EXISTING bash-tool test failures and ~88 pre-existing lint findings unrelated to this change — they fail only because aws/kubectl/docker binaries are not installed in this environment; my changes add zero new failures or lint errors.

Frontend Settings/Trust UI panel was intentionally DEFERRED as a follow-up (758-line component, styling risk); the endpoint + generated doc fully cover the artifact for Phase 0. Durable knowledge recorded in AGENTS.md (CLAUDE.md was promoted to AGENTS.md + symlinked by tooling).

What Changed

  • Added opendevops_core/agent/inventory.py (build_inventory, memoized via lru_cache) that introspects ALL_TOOLS, the bash command allowlist, the AWS read-permission matrix, and per-provider capability tiers; exposed it via a new read-only GET /api/inventory router and a generated apps/documentation/tool_inventory.md produced by scripts/gen_tool_inventory.py (do-not-edit-by-hand header).
  • Made the sources introspectable without changing behavior: promoted the kubectl/docker allowlist literals in bash_tool.py to module constants, and rewrote permissions.check_permissions() to iterate a declarative PERMISSION_PROBES table (identical dict shape and boto3 calls).
  • Corrected positioning and stale counts to the introspected reality of 26 total tools / 20 structured AWS tools across README, AGENTS.md, and the inventory doc (AWS complete, Azure CLI + skills only, GCP stub); added test_api/test_inventory.py and test_tools/test_permissions.py.

Risk Assessment

✅ Low: The change is additive and well-tested, with the two refactors being provably behavior-preserving (lambda default-arg binding and ordering preserved) and locked by new tests; the only note is a benign latent footgun in the newly-added cache.

Testing

Baseline: the new test files run green (pytest tests/test_api/test_inventory.py tests/test_tools/test_permissions.py, 6 passed). For end-user evidence I exercised the actual product surface: I called the real GET /api/inventory endpoint through the FastAPI app and captured its JSON, which shows the introspected reality — 26 total tools, 20 structured AWS tools, and the honest per-cloud capability tiers (AWS full, Azure CLI-only with no event/polling, GCP no-tool/no-CLI stub). I then ran the doc generator and confirmed it reproduces the committed tool_inventory.md byte-for-byte, demonstrating the documentation is generated from the same introspection (no drift, no hand-maintained list). The endpoint also returns JSON (not the SPA index.html), confirming the /api/ prefix correctly avoids the SPA-fallback catch-all. No screenshot was needed since the Phase-0 deliverables are an API response and a generated markdown doc, both captured as artifacts; the deferred Settings/Trust UI panel is explicitly out of scope. The pre-existing bash-tool failures noted in the intent are environmental (missing aws/kubectl/docker binaries) and unrelated to this change. All evidence consistent with intent; no new failures introduced.

Evidence: Live GET /api/inventory response (introspected trust artifact)

STATUS 200 active_provider: aws tool_count: 26 providers: {'name': 'aws', 'active': True, 'structured_tools': 20, 'cli_access': True, 'event_driven_and_polling': True} {'name': 'azure', 'active': False, 'structured_tools': 0, 'cli_access': True, 'event_driven_and_polling': False} {'name': 'gcp', 'active': False, 'structured_tools': 0, 'cli_access': False, 'event_driven_and_polling': False} aws_permission_matrix services: ['cloudwatch','cloudtrail','ecs','lambda','ec2','rds','iam','sqs','events'] kubectl subcommands: ['describe','get','logs'] docker subcommands: ['inspect','logs','ps']

{
  "active_provider": "aws",
  "tool_count": 26,
  "tools": [
    {
      "name": "get_alarms",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "List CloudWatch alarms, optionally filtered by state (OK, ALARM, INSUFFICIENT_DATA).",
      "parameters": [
        {
          "name": "state",
          "type": "str | None",
          "required": false,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_alarm_history",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "Fetch state-change history for a specific CloudWatch alarm.",
      "parameters": [
        {
          "name": "alarm_name",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 24
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_metric_data",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "Fetch raw CloudWatch metric data points for a given namespace/metric/dimensions.",
      "parameters": [
        {
          "name": "namespace",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "metric",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "dimensions",
          "type": "list[dict[str, str]]",
          "required": true,
          "default": null
        },
        {
          "name": "period",
          "type": "int",
          "required": false,
          "default": 300
        },
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 3
        },
        {
          "name": "stat",
          "type": "str",
          "required": false,
          "default": "Sum"
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_log_events",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "Fetch recent log events from a CloudWatch Logs group, with optional filter pattern.",
      "parameters": [
        {
          "name": "log_group",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "log_stream",
          "type": "str | None",
          "required": false,
          "default": null
        },
        {
          "name": "filter_pattern",
          "type": "str | None",
          "required": false,
          "default": null
        },
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 1
        },
        {
          "name": "limit",
          "type": "int",
          "required": false,
          "default": 100
        }
      ],
      "returns": "dict"
    },
    {
      "name": "describe_log_groups",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "List CloudWatch log groups, optionally filtered by name prefix.",
      "parameters": [
        {
          "name": "prefix",
          "type": "str | None",
          "required": false,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "query_logs_insights",
      "module": "opendevops_core.providers.aws.tools.cloudwatch",
      "description": "Run a CloudWatch Logs Insights structured query against a log group.",
      "parameters": [
        {
          "name": "log_group",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "query",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 1
        },
        {
          "name": "limit",
          "type": "int",
          "required": false,
          "default": 100
        }
      ],
      "returns": "dict"
    },
    {
      "name": "lookup_cloudtrail_events",
      "module": "opendevops_core.providers.aws.tools.cloudtrail",
      "description": "Look up recent CloudTrail API events.",
      "parameters": [
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 2
        },
        {
          "name": "resource_name",
          "type": "str | None",
          "required": false,
          "default": null
        },
        {
          "name": "event_name",
          "type": "str | None",
          "required": false,
          "default": null
        },
        {
          "name": "limit",
          "type": "int",
          "required": false,
          "default": 50
        }
      ],
      "returns": "dict"
    },
    {
      "name": "list_ecs_clusters",
      "module": "opendevops_core.providers.aws.tools.ecs",
      "description": "List all ECS clusters in the region with their status and active service/task counts.",
      "parameters": [],
      "returns": "dict"
    },
    {
      "name": "list_ecs_services",
      "module": "opendevops_core.providers.aws.tools.ecs",
      "description": "List ECS services in a cluster with their desired, running, and pending task counts.",
      "parameters": [
        {
          "name": "cluster",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "describe_ecs_service",
      "module": "opendevops_core.providers.aws.tools.ecs",
      "description": "Get detailed info about an ECS service including recent events and deployment status.",
      "parameters": [
        {
          "name": "cluster",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "service",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_ecs_task_logs",
      "module": "opendevops_core.providers.aws.tools.ecs",
      "description": "Fetch stdout/stderr logs for a specific ECS task from CloudWatch Logs.",
      "parameters": [
        {
          "name": "cluster",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "task_id",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "log_group",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "limit",
          "type": "int",
          "required": false,
          "default": 100
        }
      ],
      "returns": "dict"
    },
    {
      "name": "list_lambda_functions",
      "module": "opendevops_core.providers.aws.tools.lambda_",
      "description": "List all Lambda functions in the region with their runtime, memory, and timeout.",
      "parameters": [],
      "returns": "dict"
    },
    {
      "name": "get_lambda_function_config",
      "module": "opendevops_core.providers.aws.tools.lambda_",
      "description": "Get detailed configuration for a Lambda function: memory, timeout, env vars, layers, VPC.",
      "parameters": [
        {
          "name": "name",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_lambda_error_rate",
      "module": "opendevops_core.providers.aws.tools.lambda_",
      "description": "Get Lambda error count and throttle count from CloudWatch for a given time window.",
      "parameters": [
        {
          "name": "name",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 3
        }
      ],
      "returns": "dict"
    },
    {
      "name": "describe_ec2_instances",
      "module": "opendevops_core.providers.aws.tools.

... [528 bytes truncated] ...

hability).",
      "parameters": [
        {
          "name": "instance_id",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "describe_rds_instances",
      "module": "opendevops_core.providers.aws.tools.rds",
      "description": "List RDS DB instances with their status, engine, class, and multi-AZ configuration.",
      "parameters": [],
      "returns": "dict"
    },
    {
      "name": "get_rds_events",
      "module": "opendevops_core.providers.aws.tools.rds",
      "description": "Fetch RDS events log for recent database activity, failovers, maintenance, and errors.",
      "parameters": [
        {
          "name": "hours",
          "type": "int",
          "required": false,
          "default": 24
        },
        {
          "name": "db_identifier",
          "type": "str | None",
          "required": false,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_caller_identity",
      "module": "opendevops_core.providers.aws.tools.iam",
      "description": "Return the current AWS caller identity: account ID, user/role ARN, and user ID.",
      "parameters": [],
      "returns": "dict"
    },
    {
      "name": "get_iam_role_policies",
      "module": "opendevops_core.providers.aws.tools.iam",
      "description": "List policies attached to an IAM role.",
      "parameters": [
        {
          "name": "role_name",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "get_investigation_history",
      "module": "opendevops_core.tools.history",
      "description": "Get cross-session investigation analytics: top alarms investigated, top Lambda functions, recurring tool errors, and daily investigation frequency over the last N days. Never loads raw message content \u2014 all data is aggregated at the DB level.",
      "parameters": [
        {
          "name": "days",
          "type": "int",
          "required": false,
          "default": 30
        }
      ],
      "returns": "dict"
    },
    {
      "name": "search_past_investigations",
      "module": "opendevops_core.tools.history",
      "description": "Search past investigation sessions by keyword in title or message content. Returns session summaries with a short snippet \u2014 never full message bodies.",
      "parameters": [
        {
          "name": "query",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "limit",
          "type": "int",
          "required": false,
          "default": 10
        }
      ],
      "returns": "dict"
    },
    {
      "name": "run_bash_command",
      "module": "opendevops_core.tools.bash_tool",
      "description": "Run a read-only shell command and return structured output.",
      "parameters": [
        {
          "name": "command",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict[str, Any]"
    },
    {
      "name": "list_skills",
      "module": "opendevops_core.tools.skills",
      "description": "List all available investigation skills with their names and descriptions.",
      "parameters": [],
      "returns": "dict"
    },
    {
      "name": "use_skill",
      "module": "opendevops_core.tools.skills",
      "description": "Load the full investigation skill for a named incident type. The skill contains step-by-step investigation guidance, key metrics to check, log patterns to look for, and common root causes with mitigations.",
      "parameters": [
        {
          "name": "name",
          "type": "str",
          "required": true,
          "default": null
        }
      ],
      "returns": "dict"
    },
    {
      "name": "submit_investigation",
      "module": "opendevops_core.tools.final_answer",
      "description": "Submit the final structured investigation result. Call this exactly once when you have gathered sufficient evidence and reached a conclusion. Do not output a JSON block in free text \u2014 call this tool instead.",
      "parameters": [
        {
          "name": "root_cause_category",
          "type": "Literal['SYSTEM_CHANGE', 'INPUT_ANOMALY', 'RESOURCE_LIMIT', 'COMPONENT_FAILURE', 'DEPENDENCY_ISSUE', 'UNKNOWN']",
          "required": true,
          "default": null
        },
        {
          "name": "root_cause_summary",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "evidence",
          "type": "list[str]",
          "required": true,
          "default": null
        },
        {
          "name": "mitigation_steps",
          "type": "list[str]",
          "required": true,
          "default": null
        },
        {
          "name": "validation_steps",
          "type": "list[str]",
          "required": true,
          "default": null
        },
        {
          "name": "confidence",
          "type": "Literal['HIGH', 'MEDIUM', 'LOW']",
          "required": true,
          "default": null
        },
        {
          "name": "services_affected",
          "type": "list[str]",
          "required": true,
          "default": null
        },
        {
          "name": "recommended_follow_up",
          "type": "str",
          "required": true,
          "default": null
        },
        {
          "name": "follow_up_questions",
          "type": "list[str]",
          "required": true,
          "default": null
        }
      ],
      "returns": "str"
    }
  ],
  "bash_allowlist": {
    "aws": {
      "readonly_verbs": [
        "batch-get",
        "check",
        "describe",
        "filter",
        "get",
        "list",
        "lookup",
        "query",
        "scan",
        "search",
        "show",
        "view"
      ],
      "blocked_global_flags": [
        "--endpoint-url"
      ],
      "note": "aws <service> <operation> where the operation starts with a read-only verb"
    },
    "az": {
      "readonly_verbs": [
        "check",
        "describe",
        "get",
        "list",
        "query",
        "show",
        "tail",
        "version"
      ],
      "note": "az <group...> <verb> where the trailing verb is read-only"
    },
    "kubectl": {
      "subcommands": [
        "describe",
        "get",
        "logs"
      ]
    },
    "docker": {
      "subcommands": [
        "inspect",
        "logs",
        "ps"
      ]
    },
    "timeout_seconds": 30,
    "max_output_chars": 4000,
    "shell_chaining": "blocked"
  },
  "aws_permission_matrix": [
    {
      "service": "cloudwatch",
      "boto3_service": "cloudwatch",
      "operation": "describe_alarms"
    },
    {
      "service": "cloudtrail",
      "boto3_service": "cloudtrail",
      "operation": "lookup_events"
    },
    {
      "service": "ecs",
      "boto3_service": "ecs",
      "operation": "list_clusters"
    },
    {
      "service": "lambda",
      "boto3_service": "lambda",
      "operation": "list_functions"
    },
    {
      "service": "ec2",
      "boto3_service": "ec2",
      "operation": "describe_instances"
    },
    {
      "service": "rds",
      "boto3_service": "rds",
      "operation": "describe_db_instances"
    },
    {
      "service": "iam",
      "boto3_service": "sts",
      "operation": "get_caller_identity"
    },
    {
      "service": "sqs",
      "boto3_service": "sqs",
      "operation": "list_queues"
    },
    {
      "service": "events",
      "boto3_service": "events",
      "operation": "list_rules"
    }
  ],
  "providers": [
    {
      "name": "aws",
      "active": true,
      "structured_tools": 20,
      "cli_access": true,
      "event_driven_and_polling": true
    },
    {
      "name": "azure",
      "active": false,
      "structured_tools": 0,
      "cli_access": true,
      "event_driven_and_polling": false
    },
    {
      "name": "gcp",
      "active": false,
      "structured_tools": 0,
      "cli_access": false,
      "event_driven_and_polling": false
    }
  ]
}
Evidence: Generated tool_inventory.md doc page (regenerates byte-identical to committed)

# Tool & Permission Inventory > Generated file — do not edit by hand. ... ## Capability by cloud | Cloud | Structured SDK tools | CLI access (bash tool) | Event-driven + polling | | AWS (active) | 20 | yes | yes | | AZURE | 0 | yes | no | | GCP | 0 | no | no | Active provider: aws · total registered tools: 26.

# Tool & Permission Inventory

> **Generated file — do not edit by hand.** Produced by `apps/backend/scripts/gen_tool_inventory.py` from the live code (`opendevops_core.agent.inventory.build_inventory`), the same source that backs the read-only `GET /api/inventory` endpoint. Regenerate with `cd apps/backend && uv run python scripts/gen_tool_inventory.py`.

This is the trust artifact: exactly what the agent can inspect — every registered tool and its parameters, the read-only bash command allowlist, the AWS read-permission probe, and the per-cloud capability tiers. Everything is read-only.

## Capability by cloud

| Cloud | Structured SDK tools | CLI access (`bash` tool) | Event-driven + polling |
|---|---|---|---|
| **AWS** (active) | 20 | yes | yes |
| **AZURE** | 0 | yes | no |
| **GCP** | 0 | no | no |

Active provider: **aws** · total registered tools: **26**.

## Registered tools

### `cloudwatch`

#### `get_alarms` → `dict`

List CloudWatch alarms, optionally filtered by state (OK, ALARM, INSUFFICIENT_DATA).

| Param | Type | Default |
|---|---|---|
| `state` | `str \| None` | `None` |

#### `get_alarm_history` → `dict`

Fetch state-change history for a specific CloudWatch alarm.

| Param | Type | Default |
|---|---|---|
| `alarm_name` | `str` | required |
| `hours` | `int` | `24` |

#### `get_metric_data` → `dict`

Fetch raw CloudWatch metric data points for a given namespace/metric/dimensions.

| Param | Type | Default |
|---|---|---|
| `namespace` | `str` | required |
| `metric` | `str` | required |
| `dimensions` | `list[dict[str, str]]` | required |
| `period` | `int` | `300` |
| `hours` | `int` | `3` |
| `stat` | `str` | `'Sum'` |

#### `get_log_events` → `dict`

Fetch recent log events from a CloudWatch Logs group, with optional filter pattern.

| Param | Type | Default |
|---|---|---|
| `log_group` | `str` | required |
| `log_stream` | `str \| None` | `None` |
| `filter_pattern` | `str \| None` | `None` |
| `hours` | `int` | `1` |
| `limit` | `int` | `100` |

#### `describe_log_groups` → `dict`

List CloudWatch log groups, optionally filtered by name prefix.

| Param | Type | Default |
|---|---|---|
| `prefix` | `str \| None` | `None` |

#### `query_logs_insights` → `dict`

Run a CloudWatch Logs Insights structured query against a log group.

| Param | Type | Default |
|---|---|---|
| `log_group` | `str` | required |
| `query` | `str` | required |
| `hours` | `int` | `1` |
| `limit` | `int` | `100` |

### `cloudtrail`

#### `lookup_cloudtrail_events` → `dict`

Look up recent CloudTrail API events.

| Param | Type | Default |
|---|---|---|
| `hours` | `int` | `2` |
| `resource_name` | `str \| None` | `None` |
| `event_name` | `str \| None` | `None` |
| `limit` | `int` | `50` |

### `ecs`

#### `list_ecs_clusters` → `dict`

List all ECS clusters in the region with their status and active service/task counts.

*No parameters.*

#### `list_ecs_services` → `dict`

List ECS services in a cluster with their desired, running, and pending task counts.

| Param | Type | Default |
|---|---|---|
| `cluster` | `str` | required |

#### `describe_ecs_service` → `dict`

Get detailed info about an ECS service including recent events and deployment status.

| Param | Type | Default |
|---|---|---|
| `cluster` | `str` | required |
| `service` | `str` | required |

#### `get_ecs_task_logs` → `dict`

Fetch stdout/stderr logs for a specific ECS task from CloudWatch Logs.

| Param | Type | Default |
|---|---|---|
| `cluster` | `str` | required |
| `task_id` | `str` | required |
| `log_group` | `str` | required |
| `limit` | `int` | `100` |

### `lambda_`

#### `list_lambda_functions` → `dict`

List all Lambda functions in the region with their runtime, memory, and timeout.

*No parameters.*

#### `get_lambda_function_config` → `dict`

Get detailed configuration for a Lambda function: memory, timeout, env vars, layers, VPC.

| Param | Type | Default |
|---|---|---|
| `name` | `str` | required |

#### `get_lambda_error_rate` → `dict`

Get Lambda error count and throttle count from CloudWatch for a given time window.

| Param | Type | Default |
|---|---|---|
| `name` | `str` | required |
| `hours` | `int` | `3` |

### `ec2`

#### `describe_ec2_instances` → `dict`

List EC2 instances with their state, type, and tags. Optionally filter by state or tag.

| Param | Type | Default |
|---|---|---|
| `filters` | `list[dict[str, Any]] \| None` | `None` |

#### `get_ec2_system_status` → `dict`

Get EC2 instance status checks (system reachability and instance reachability).

| Param | Type | Default |
|---|---|---|
| `instance_id` | `str` | required |

### `rds`

#### `describe_rds_instances` → `dict`

List RDS DB instances with their status, engine, class, and multi-AZ configuration.

*No parameters.*

#### `get_rds_events` → `dict`

Fetch RDS events log for recent database activity, failovers, maintenance, and errors.

| Param | Type | Default |
|---|---|---|
| `hours` | `int` | `24` |
| `db_identifier` | `str \| None` | `None` |

### `iam`

#### `get_caller_identity` → `dict`

Return the current AWS caller identity: account ID, user/role ARN, and user ID.

*No parameters.*

#### `get_iam_role_policies` → `dict`

List policies attached to an IAM role.

| Param | Type | Default |
|---|---|---|
| `role_name` | `str` | required |

### `history`

#### `get_investigation_history` → `dict`

Get cross-session investigation analytics: top alarms investigated, top Lambda functions, recurring tool errors, and daily investigation frequency over the last N days. Never loads raw message content — all data is aggregated at the DB level.

| Param | Type | Default |
|---|---|---|
| `days` | `int` | `30` |

#### `search_past_investigations` → `dict`

Search past investigation sessions by keyword in title or message content. Returns session summaries with a short snippet — never full message bodies.

| Param | Type | Default |
|---|---|---|
| `query` | `str` | required |
| `limit` | `int` | `10` |

### `bash_tool`

#### `run_bash_command` → `dict[str, Any]`

Run a read-only shell command and return structured output.

| Param | Type | Default |
|---|---|---|
| `command` | `str` | required |

### `skills`

#### `list_skills` → `dict`

List all available investigation skills with their names and descriptions.

*No parameters.*

#### `use_skill` → `dict`

Load the full investigation skill for a named incident type. The skill contains step-by-step investigation guidance, key metrics to check, log patterns to look for, and common root causes with mitigations.

| Param | Type | Default |
|---|---|---|
| `name` | `str` | required |

### `final_answer`

#### `submit_investigation` → `str`

Submit the final structured investigation result. Call this exactly once when you have gathered sufficient evidence and reached a conclusion. Do not output a JSON block in free text — call this tool instead.

| Param | Type | Default |
|---|---|---|
| `root_cause_category` | `Literal['SYSTEM_CHANGE', 'INPUT_ANOMALY', 'RESOURCE_LIMIT', 'COMPONENT_FAILURE', 'DEPENDENCY_ISSUE', 'UNKNOWN']` | required |
| `root_cause_summary` | `str` | required |
| `evidence` | `list[str]` | required |
| `mitigation_steps` | `list[str]` | required |
| `validation_steps` | `list[str]` | required |
| `confidence` | `Literal['HIGH', 'MEDIUM', 'LOW']` | required |
| `services_affected` | `list[str]` | required |
| `recommended_follow_up` | `str` | required |
| `follow_up_questions` | `list[str]` | required |

## Bash command allowlist

`run_bash_command` runs only read-only commands, validated against this allowlist before execution. Shell chaining is **blocked**, `shell=True` is never used, output is capped at 4000 chars, and every command has a hard 30s timeout.

- **aws** — aws <service> <operation> where the operation starts with a read-only verb. Read-only verbs: `batch-get`, `check`, `describe`, `filter`, `get`, `list`, `lookup`, `query`, `scan`, `search`, `show`, `view`. Blocked global flags: `--endpoint-url`.
- **az** — az <group...> <verb> where the trailing verb is read-only. Read-only verbs: `check`, `describe`, `get`, `list`, `query`, `show`, `tail`, `version`.
- **kubectl** — subcommands: `describe`, `get`, `logs`.
- **docker** — subcommands: `inspect`, `logs`, `ps`.

## AWS read-permission matrix

One lightweight read call per service verifies the agent's credentials (surfaced by the in-app permission checker).

| Service | boto3 client | Read operation |
|---|---|---|
| cloudwatch | `cloudwatch` | `describe_alarms` |
| cloudtrail | `cloudtrail` | `lookup_events` |
| ecs | `ecs` | `list_clusters` |
| lambda | `lambda` | `list_functions` |
| ec2 | `ec2` | `describe_instances` |
| rds | `rds` | `describe_db_instances` |
| iam | `sts` | `get_caller_identity` |
| sqs | `sqs` | `list_queues` |
| events | `events` | `list_rules` |

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ apps/core/src/opendevops_core/agent/inventory.py:127 - _provider_capabilities() calls AzureProvider().tools() and GcpProvider().tools() on every request to build the inventory. Both stub implementations emit logger.warning("... provider not yet implemented ..."). Since GET /api/inventory is public and unauthenticated, each hit produces two WARNING log lines that falsely imply something is wrong, and could flood logs if the endpoint is polled (e.g. by a UI). Consider counting structured tools via a path that doesn't trigger the stub warnings, or demoting those provider stub logs to debug.
  • ℹ️ apps/core/src/opendevops_core/agent/inventory.py:118 - structured_tools is introspected, but cli_access and event_driven_and_polling are hand-coded boolean literals in the per-provider tuple. This is the one part of the 'inventory never drifts' artifact that is not sourced from live code, so e.g. adding az/kubectl support for GCP, or an Azure event loop, would silently leave these flags stale. Acceptable for Phase 0 since these aren't trivially introspectable, but worth a comment noting they're manually maintained.

🔧 Fix: memoize build_inventory and document manual provider flags
1 info still open:

  • ℹ️ apps/core/src/opendevops_core/agent/inventory.py:140 - build_inventory() is memoized with functools.lru_cache(maxsize=1) and returns the same dict instance on every call. It is handed straight back from the public GET /api/inventory endpoint and to the doc generator. No current caller mutates it, so this is safe today, but because the cached object is a shared mutable dict exposed directly, any future in-place mutation by a consumer (e.g. adding a field before returning) would silently poison the cache for all subsequent requests. Cheap guard: return a deep copy, or document that callers must treat the result as read-only.
✅ **Test** - passed

✅ No issues found.

  • uv run pytest tests/test_api/test_inventory.py tests/test_tools/test_permissions.py — 6 passed
  • Hit the live endpoint via ASGITransport GET /api/inventory (status 200) and saved the full JSON response
  • Confirmed honest tiers from the live response: AWS structured_tools=20/cli/event-driven, Azure=0 structured/cli-only/no-events, GCP=0 structured/no-cli (stub), total tool_count=26
  • Verified permission matrix and kubectl/docker allowlist constants are reflected in the endpoint payload
  • Ran uv run python scripts/gen_tool_inventory.py and diffed against the committed apps/documentation/tool_inventory.md — empty diff, proving the doc is generated and in sync
  • Confirmed worktree left clean (regenerated doc restored via git checkout)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

AhmadHammad21 and others added 3 commits June 25, 2026 23:43
…est cloud tiers

Add a trust artifact that exposes exactly what the agent can do, sourced live
from the code so it never drifts:

- `opendevops_core.agent.inventory.build_inventory()` introspects `ALL_TOOLS`
  (name, description, type-hinted params), the bash command allowlist, the AWS
  read-permission matrix, and per-provider capability tiers.
- Read-only `GET /api/inventory` endpoint (SPA-safe `/api/` prefix).
- Generated `apps/documentation/tool_inventory.md` via
  `scripts/gen_tool_inventory.py` (never hand-edited).

To keep these non-drifting and behavior-preserving:
- Promote the kubectl/docker allowlist sets to module constants in `bash_tool.py`
  (`_KUBECTL_SUBCOMMANDS`, `_DOCKER_SUBCOMMANDS`) — same values, single source.
- Refactor `permissions.check_permissions()` to iterate a declarative
  `PERMISSION_PROBES` table — identical behavior, now introspectable.

Honest multi-cloud positioning in README + the doc page: AWS complete (20
structured tools + CLI), Azure CLI + 4 runbook skills (no structured SDK tools,
no event-driven/polling loop), GCP not implemented (stub returns no tools).
Corrects stale tool counts (actual: 26 total / 20 AWS structured).

Tests assert the endpoint reflects `ALL_TOOLS`, the allowlist constants, the
permission probes, and the honest provider tiers.

Frontend Settings/Trust panel deferred as a follow-up (large file, styling risk);
endpoint + doc page cover the artifact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The merged evidence pack (PR #67) added a `hypotheses: list[dict]` parameter to
submit_investigation. The introspected inventory picks this up automatically;
regenerate the generated doc page so it stays in sync with the merged tool
surface. Confirms both changesets coexist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AhmadHammad21
AhmadHammad21 merged commit 7a3e247 into main Jun 25, 2026
2 checks passed
@AhmadHammad21
AhmadHammad21 deleted the fm/odo-inv-x4 branch June 25, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant