Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/agent-run-extension-seams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@hyperdx/api': minor
'@hyperdx/app': minor
---

The managed-agent alert flow now exposes fail-open extension seams
(`onProvisionAgent`, `onSessionStart`, `onBeforeDelivery`, `resolveAnthropicKey`)
with a downstream-owned registration point and an `AgentRun.metadata` field, so
downstream distributions can extend investigations (e.g. notebook tracking) and
swap the agent's system and kickoff prompts wholesale without editing core
files. Claude webhooks now also honour their user-editable `body` template as
the agent's kickoff prompt (previously documented but ignored), falling back to
the built-in enriched payload for empty or broken templates.

The Anthropic API key for managed agents is now resolved from the environment
(`AI_API_KEY` with `AI_PROVIDER=anthropic`, or the legacy `ANTHROPIC_API_KEY`),
which is the sensible default for self-hosted deployments. The per-team,
UI-managed key (previously stored encrypted in MongoDB) has been removed from
open source and is now a downstream concern, injected through the
`resolveAnthropicKey` extension seam. With no extensions registered and the
default webhook body, behaviour is otherwise unchanged.
23 changes: 23 additions & 0 deletions .changeset/claude-managed-agents-webhook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
'@hyperdx/common-utils': minor
'@hyperdx/api': minor
'@hyperdx/app': minor
---

feat: add Claude Managed Agents webhook template with enriched, agent-ready payload

Adds a "Claude Managed Agents" webhook service type that posts an enriched,
agent-ready JSON payload (sent identically to a Generic webhook). The pre-built
body carries structured alert context (status, type, comparator, threshold,
current value, group key, source query, team id, time range) and a prompt
instructing the agent to investigate via its pre-configured ClickStack MCP
server and post a root-cause summary. No MCP URL or auth is sent in the
payload — the agent reaches ClickStack through the MCP server declared on the
agent with credentials held in a vault.

These enriched template variables (`{{status}}`, `{{alertType}}`,
`{{comparator}}`, `{{threshold}}`, `{{value}}`, `{{groupKey}}`,
`{{sourceQuery}}`, `{{teamId}}`, `{{alertId}}`, `{{note}}`) are also available to
Generic webhook bodies for pre-agent routing and dedup. The alert's freeform
`note` is surfaced as `context.runbook` so a runbook link attached to the alert
reaches the agent.
17 changes: 17 additions & 0 deletions .changeset/managed-agents-provisioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@hyperdx/api': minor
'@hyperdx/app': minor
---

feat: provision Claude Managed Agents from the HyperDX UI

Add a Managed Agents section under Team Settings → API & Agents (gated by
`HDX_MANAGED_AGENTS_ENABLED` / `NEXT_PUBLIC_HDX_MANAGED_AGENTS_ENABLED`). The
Anthropic API key is read from the server environment (`AI_API_KEY` with
`AI_PROVIDER=anthropic`, or the legacy `ANTHROPIC_API_KEY`); per-team,
UI-managed key storage is a downstream (EE) concern injected via the
`resolveAnthropicKey` extension seam. A team can provision an opinionated
ClickStack SRE agent in one click — HyperDX creates the Anthropic environment,
vault (with the provisioning user's ClickStack access key as the MCP
credential), and agent with the ClickStack MCP server pre-configured, then
stores the references for management.
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ before stopping.
them). Use the shared helpers in
`packages/api/src/utils/instrumentation.ts`. See
[`agent_docs/observability.md`](agent_docs/observability.md).
8. **EE extensibility**: this repo is upstream of an enterprise fork — build

@brandon-pereira brandon-pereira Jul 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: We should not reference EE from OSS, same for the PR description.

extension seams, not fork-edited function bodies. See "Designing for
Downstream (EE) Extensibility" below.

## Running Tests

Expand Down Expand Up @@ -169,6 +172,62 @@ make dev-e2e FILE=navigation REPORT=1 # Open HTML report after run
make dev-e2e-clean # Remove test artifacts
```

## Designing for Downstream (EE) Extensibility

HyperDX has an enterprise fork (hyperdx-ee) that receives regular upstream
merges from this repo. Every inline edit EE makes to an OSS file becomes a
merge conflict on the next upstream merge — the EE "conflict resolution"
agent exists to clean those up, and we would rather not need it. Design OSS
features so EE can extend them **without editing OSS function bodies,
schemas, or test files**:

1. **Extension seams over inline edits.** When a feature has lifecycle
points downstream will plausibly hook into (a session starting, a
delivery going out), expose a typed hook registry with no-op defaults —
see `packages/api/src/services/agentRunExtensions.ts` for the reference
pattern. Hook runners must be fail-open (an extension error never breaks
the core flow) and instrumented (a span per hook invocation plus an
outcome counter).
2. **Downstream-owned override files.** Registration happens in designated
files that upstream commits to never editing after creation — see
`packages/api/src/extensions/index.ts`. EE replaces the file's body
wholesale, so upstream merges never conflict on it.
3. **Additive, schema-free extension data.** Give downstream a
`metadata: Mixed` field on models it needs to decorate (see
`AgentRun.metadata`) instead of having it add typed fields to OSS
schemas.
4. **Options objects / optional trailing parameters** on exported functions
downstream feeds data into — adding an optional key never conflicts;
reshaping a signature does.
5. **Swappable defaults.** Operator-visible defaults downstream may want to
replace wholesale — prompts, templates, message copy — should resolve
through a hook (see `onProvisionAgent`'s `systemPrompt` and
`onSessionStart`'s `promptOverride` in `agentRunExtensions.ts`), not sit as
hardcoded constants at the call site.
6. **OSS tests never require EE edits.** Test the hook contract in OSS with
a fake extension; EE tests its own extensions in its own files.

When building a feature EE is likely to extend, add the seam in the same PR.
Retrofitting a seam after EE has already forked the file is exactly the
conflict this section exists to prevent.

### Seam exports and `knip`

A seam contract (the interfaces/types EE implements against — e.g. the
`Agent*Result` types in `agentRunExtensions.ts`) is exported public API that
has **no importer inside this repo by design**: OSS ships no extensions, so
only the downstream fork consumes it. Our `knip` check (run in the pre-commit
hook and CI) would otherwise flag those exports as unused.

The rule: mark a genuinely-downstream-only export with a JSDoc `@public` tag and
a one-line reason. `knip.json` sets `"tags": ["-public"]`, so `@public`-tagged
exports are treated as intentional public API rather than dead code. This is
scoped per-export — an export that is used nowhere at all (not even in-file, not
tagged) is still reported, so the check keeps its teeth. Do **not** reach for a
blanket `ignoreExportsUsedInFile` category toggle to silence a seam export; tag
the specific export instead. Conversely, an export that only turned out to be
unused (no EE consumer planned) should be **unexported or removed**, not tagged.

## Important Context

- **Authentication**: Passport.js with team-based access control
Expand Down
Loading
Loading