# CrewAI

`HighflameCrewHooks` integrates Highflame Shield into CrewAI agents by registering with CrewAI's execution hooks registry. It intercepts every LLM call and tool execution without modifying task or agent definitions.

Available for **Python** and **TypeScript**.

### Installation

{% tabs %}
{% tab title="Python" %}

```bash
pip install 'highflame[crewai]'
```

{% endtab %}

{% tab title="TypeScript" %}

```bash
npm install @highflame/sdk
```

The TypeScript integration is interface-based — no CrewAI JavaScript SDK is required. Pass any object conforming to the `CrewHooksRegistry` interface.
{% endtab %}
{% endtabs %}

### Basic Usage

{% tabs %}
{% tab title="Python" %}

```python
from highflame import Highflame
from highflame.integrations.crewai import HighflameCrewHooks

client = Highflame(api_key="hf_sk_...")
hooks = HighflameCrewHooks(client, mode="enforce")
hooks.register()

result = crew.kickoff()
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Highflame } from "@highflame/sdk";
import { HighflameCrewHooks } from "@highflame/sdk/integrations/crewai";

const client = new Highflame({ apiKey: "hf_sk_..." });
const hooks = new HighflameCrewHooks(client, { mode: "enforce" });
hooks.register(registry);

const result = await crew.kickoff();
```

{% endtab %}
{% endtabs %}

On a policy violation, `HighflameCrewHooks` raises `BlockedError` before the blocked operation completes.

### Constructor

{% tabs %}
{% tab title="Python" %}

```python
HighflameCrewHooks(
    client: Highflame,
    *,
    mode: str = "enforce",
    session_id: str | None = None,
)
```

| Parameter    | Type          | Default     | Description                                                          |
| ------------ | ------------- | ----------- | -------------------------------------------------------------------- |
| `client`     | `Highflame`   | required    | Initialized Highflame client                                         |
| `mode`       | `str`         | `"enforce"` | Enforcement mode: `"enforce"`, `"monitor"`, or `"alert"`             |
| `session_id` | `str \| None` | `None`      | Static session ID. If not set, uses `context.crew.id` automatically. |
| {% endtab %} |               |             |                                                                      |

{% tab title="TypeScript" %}

```typescript
new HighflameCrewHooks(client: Highflame, options?: {
  mode?: "enforce" | "monitor" | "alert";
  sessionId?: string;
  sessionIdKey?: string;
})
```

| Parameter      | Type                  | Default     | Description                                                              |
| -------------- | --------------------- | ----------- | ------------------------------------------------------------------------ |
| `client`       | `Highflame`           | required    | Initialized Highflame client                                             |
| `mode`         | `string`              | `"enforce"` | Enforcement mode: `"enforce"`, `"monitor"`, or `"alert"`                 |
| `sessionId`    | `string \| undefined` | `undefined` | Static session ID. If not set, reads `crew[sessionIdKey]` automatically. |
| `sessionIdKey` | `string`              | `"id"`      | Key to read from the crew object for dynamic session ID resolution.      |
| {% endtab %}   |                       |             |                                                                          |
| {% endtabs %}  |                       |             |                                                                          |

### Session ID Resolution

The hooks resolve the session ID in this order:

1. Static `session_id` / `sessionId` — if provided at construction time
2. `context.crew.id` (Python) / `crew[sessionIdKey]` (TypeScript) — automatically derived from the running crew
3. `None` / `undefined` — no session tracking

Using the crew ID as the session ID is the right default for per-crew risk tracking. Pass an explicit `session_id` when you need to correlate across multiple crew runs.

### Hooks

`HighflameCrewHooks` registers four hooks with the CrewAI hooks registry:

#### `before_llm_call` / `beforeLlmCall`

Fires before the LLM is called. Extracts the last user message from the message list and evaluates it as a prompt.

* Content type: `"prompt"`, action: `"process_prompt"`
* On deny: raises `BlockedError` before the LLM is invoked

#### `after_llm_call` / `afterLlmCall`

Fires after the LLM produces a response. Evaluates `context.response` as a model response.

* Content type: `"response"`, action: `"process_prompt"`
* On deny: raises `BlockedError` before the response is returned

#### `before_tool_call` / `beforeToolCall`

Fires before a tool executes. Evaluates the tool call using the tool name and input.

* Content type: `"tool_call"`, action: `"call_tool"`
* On deny: raises `BlockedError`, tool is not called

#### `after_tool_call` / `afterToolCall`

Fires after a tool returns. Evaluates the tool result as a tool response.

* Content type: `"response"`, action: `"call_tool"`
* On deny: raises `BlockedError`, result is not returned to the agent

### Registration Patterns

{% tabs %}
{% tab title="Python" %}
**Explicit register/unregister**

```python
hooks = HighflameCrewHooks(client, mode="enforce")
hooks.register()

try:
    result = crew.kickoff()
finally:
    hooks.unregister()
```

**Context manager (recommended)**

```python
with HighflameCrewHooks(client, mode="enforce") as hooks:
    result = crew.kickoff()
```

The context manager registers on entry and unregisters on exit, even if an exception is raised.
{% endtab %}

{% tab title="TypeScript" %}
**Explicit register/unregister**

```typescript
const hooks = new HighflameCrewHooks(client, { mode: "enforce" });
hooks.register(registry);

try {
  const result = await crew.kickoff();
} finally {
  hooks.unregister(registry);
}
```

The TypeScript integration takes the registry as an explicit parameter rather than using a global registry, making it suitable for environments where multiple crews run concurrently.
{% endtab %}
{% endtabs %}

### Enforcement Modes

{% tabs %}
{% tab title="Python" %}

```python
hooks = HighflameCrewHooks(client, mode="enforce")  # block violations (default)
hooks = HighflameCrewHooks(client, mode="monitor")  # observe without blocking
hooks = HighflameCrewHooks(client, mode="alert")    # allow but trigger alerting
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
new HighflameCrewHooks(client, { mode: "enforce" })  // block violations (default)
new HighflameCrewHooks(client, { mode: "monitor" })  // observe without blocking
new HighflameCrewHooks(client, { mode: "alert" })    // allow but trigger alerting
```

{% endtab %}
{% endtabs %}

In `monitor` and `alert` mode, `BlockedError` is never raised. The crew proceeds normally regardless of policy decisions.

### Complete Example

{% tabs %}
{% tab title="Python" %}

```python
from crewai import Agent, Task, Crew, Process
from highflame import Highflame, BlockedError
from highflame.integrations.crewai import HighflameCrewHooks

client = Highflame(api_key="hf_sk_...")

researcher = Agent(
    role="Research Analyst",
    goal="Research the given topic thoroughly",
    tools=[web_search_tool, file_read_tool],
)

research_task = Task(
    description="Research the latest developments in {topic}",
    agent=researcher,
)

crew = Crew(
    agents=[researcher],
    tasks=[research_task],
    process=Process.sequential,
)

try:
    with HighflameCrewHooks(client, mode="enforce") as hooks:
        result = crew.kickoff(inputs={"topic": user_topic})
except BlockedError as e:
    print(f"Crew execution blocked: {e.response.policy_reason}")
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Highflame, BlockedError } from "@highflame/sdk";
import { HighflameCrewHooks } from "@highflame/sdk/integrations/crewai";

const client = new Highflame({ apiKey: "hf_sk_..." });
const hooks = new HighflameCrewHooks(client, { mode: "enforce" });

hooks.register(crew.hooks);

try {
  const result = await crew.kickoff({ inputs: { topic: userTopic } });
} catch (e) {
  if (e instanceof BlockedError) {
    console.log(`Crew execution blocked: ${e.response.policy_reason}`);
  }
} finally {
  hooks.unregister(crew.hooks);
}
```

{% endtab %}
{% endtabs %}

### Session Tracking Across Crew Runs

When running multiple crews for the same user session, pass an explicit `session_id` to correlate risk signals:

{% tabs %}
{% tab title="Python" %}

```python
session_id = f"user_{user_id}_session_{conversation_id}"

with HighflameCrewHooks(client, session_id=session_id) as hooks:
    result = crew.kickoff()
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const sessionId = `user_${userId}_session_${conversationId}`;
const hooks = new HighflameCrewHooks(client, { sessionId });
hooks.register(registry);
```

{% endtab %}
{% endtabs %}

### Error Handling

{% tabs %}
{% tab title="Python" %}

```python
from highflame import BlockedError

try:
    with HighflameCrewHooks(client) as hooks:
        result = crew.kickoff()
except BlockedError as e:
    print(f"Blocked: {e.response.policy_reason}")
    print(f"Signals: {[s.name for s in e.response.signals]}")
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { BlockedError } from "@highflame/sdk";

try {
  const result = await crew.kickoff();
} catch (e) {
  if (e instanceof BlockedError) {
    console.log(`Blocked: ${e.response.policy_reason}`);
    console.log(`Signals: ${e.response.signals}`);
  }
}
```

{% endtab %}
{% endtabs %}

### Requirements

{% tabs %}
{% tab title="Python" %}

| Package      | Minimum Version |
| ------------ | --------------- |
| `crewai`     | 1.0+            |
| `highflame`  | latest          |
| {% endtab %} |                 |

{% tab title="TypeScript" %}

| Package     | Minimum Version |
| ----------- | --------------- |
| `highflame` | latest          |

No CrewAI peer dependency required for TypeScript — the integration uses a structural `CrewHooksRegistry` interface.
{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.highflame.ai/api-reference/sdk/shield/integrations/crewai.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
