> ## Documentation Index
> Fetch the complete documentation index at: https://docs.valyu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# DeepResearch HITL

> Human-in-the-loop checkpoints for deep research using the Valyu TypeScript SDK

Enable checkpoints that pause deep research at key decision points, allowing users to review and guide the research process.

<Note>
  For conceptual overview, checkpoint response shapes, and lifecycle details, see the [HITL Guide](/guides/deepresearch-hitl). This page focuses on TypeScript SDK usage.
</Note>

<Warning>
  HITL is only available for individual deep research tasks. It is **not available for batch requests**.
</Warning>

## Quick start

```typescript theme={null}
import Valyu from "valyu-js";

const client = new Valyu();

// Create a task with all checkpoints enabled
const task = await client.deepresearch.create({
  query: "Analyze the competitive landscape of AI chip manufacturers",
  mode: "heavy",
  hitl: {
    planningQuestions: true,
    planReview: true,
    sourceReview: true,
    outlineReview: true,
  },
});

const taskId = task.deepresearch_id!;
console.log(`Task created: ${taskId}`);
```

## Enabling HITL

Pass a `HitlConfig` object to `create()`. All fields are optional booleans (default `false`):

```typescript theme={null}
// Enable selective checkpoints
const task = await client.deepresearch.create({
  query: "...",
  mode: "heavy",
  hitl: {
    planningQuestions: true,
    sourceReview: true,
  },
});

// Enable all checkpoints
const task2 = await client.deepresearch.create({
  query: "...",
  mode: "heavy",
  hitl: {
    planningQuestions: true,
    planReview: true,
    sourceReview: true,
    outlineReview: true,
  },
});
```

### Available checkpoints

| Checkpoint          | Phase         | When it fires                                         |
| ------------------- | ------------- | ----------------------------------------------------- |
| `planningQuestions` | Pre-research  | Before research — clarifying questions for the user   |
| `planReview`        | Pre-research  | After planning — user reviews the research plan       |
| `sourceReview`      | Post-research | After research — user filters sources by domain       |
| `outlineReview`     | Post-research | After source review — user reviews the report outline |

Checkpoints fire in order. Only enabled checkpoints fire.

## Polling for checkpoints

Poll with `status()` and check for `awaiting_input` or `paused`:

```typescript theme={null}
while (true) {
  const status = await client.deepresearch.status(taskId);

  if (
    (status.status === "awaiting_input" || status.status === "paused") &&
    status.interaction
  ) {
    const { type, data, interaction_id } = status.interaction;
    console.log(`Checkpoint: ${type}`, data);

    // Build and send response (see sections below)
    const response = buildResponse(type, data);
    await client.deepresearch.respond(taskId, interaction_id, response);
  } else if (
    status.status === "completed" ||
    status.status === "failed" ||
    status.status === "cancelled"
  ) {
    break;
  }

  await new Promise((r) => setTimeout(r, 5000));
}
```

### Status values

| Status             | Meaning                                                              |
| ------------------ | -------------------------------------------------------------------- |
| `"awaiting_input"` | Checkpoint active, container holding capacity, fast resume           |
| `"paused"`         | Checkpoint timed out (5 min), state saved, respond anytime to resume |
| `"running"`        | Research or writing in progress                                      |
| `"queued"`         | Re-enqueued after responding to a paused task                        |

## Responding to checkpoints

### `respond()`

```typescript theme={null}
const result = await client.deepresearch.respond(
  taskId,
  "int_abc123",
  { approved: true }
);
console.log(result.status); // "running" or "queued"
```

**Parameters:**

| Parameter       | Type                  | Description                                            |
| --------------- | --------------------- | ------------------------------------------------------ |
| `taskId`        | `string`              | The deep research task ID                              |
| `interactionId` | `string`              | From `status.interaction.interaction_id`               |
| `response`      | `Record<string, any>` | Response data matching the checkpoint type (see below) |

**Returns:** `DeepResearchRespondResponse` with `success`, `status`, `deepresearch_id`, `error`.

### Planning questions response

```typescript theme={null}
// interaction.data contains:
// {
//   questions: [
//     { question: "What regions?", context: "..." },
//     { question: "Specific competitors?" }
//   ]
// }

await client.deepresearch.respond(taskId, interaction_id, {
  answers: [
    { question: "What regions?", answer: "North America and EU" },
    { question: "Specific competitors?", answer: "NVIDIA, AMD, Intel" },
  ],
});
```

### Plan review response

```typescript theme={null}
// interaction.data contains:
// {
//   plan: "I'll research this topic by...",
//   estimated_steps: 15,
//   research_areas: ["Market size", "Competitive landscape", ...]
// }

// Approve
await client.deepresearch.respond(taskId, interaction_id, {
  approved: true,
});

// Request modifications
await client.deepresearch.respond(taskId, interaction_id, {
  approved: false,
  modifications: "Focus more on supply chain analysis",
});
```

### Source review response

```typescript theme={null}
// interaction.data contains:
// {
//   domains: [
//     {
//       domain: "sec.gov",
//       source_count: 8,
//       avg_relevance_score: 0.87,
//       sources: [{ source_id: 1, title: "...", url: "...", relevance_score: 0.92 }],
//       ai_recommendation: "include"
//     },
//     ...
//   ],
//   total_sources: 42
// }

await client.deepresearch.respond(taskId, interaction_id, {
  included_domains: ["sec.gov", "plos.org"],
  excluded_domains: ["example.com"],
});
```

Domains not listed fall back to the AI recommendation. Pass empty arrays `[]` to accept all AI recommendations.

### Outline review response

```typescript theme={null}
// interaction.data contains:
// {
//   outline: "1. Introduction\n2. Market Analysis\n...",
//   sections: [
//     { title: "Introduction", description: "...", estimated_length: "short" },
//     { title: "Market Analysis", description: "...", estimated_length: "long" },
//     ...
//   ]
// }

// Approve
await client.deepresearch.respond(taskId, interaction_id, {
  approved: true,
});

// Request modifications
await client.deepresearch.respond(taskId, interaction_id, {
  approved: false,
  modifications: "Add a regulatory risks section after Market Analysis",
});
```

## Using `wait()` with HITL

The `wait()` method supports an `onInteraction` callback that automatically handles HITL checkpoints during polling:

```typescript theme={null}
import Valyu from "valyu-js";

const client = new Valyu();

const task = await client.deepresearch.create({
  query: "Analyze the competitive landscape of AI chip manufacturers",
  mode: "heavy",
  hitl: {
    planningQuestions: true,
    planReview: true,
    sourceReview: true,
    outlineReview: true,
  },
});

const result = await client.deepresearch.wait(task.deepresearch_id!, {
  onInteraction: async (interaction) => {
    console.log(`Checkpoint: ${interaction.type}`);

    if (interaction.type === "planning_questions") {
      return {
        answers: interaction.data.questions.map((q: any) => ({
          question: q.question,
          answer: "Focus on NVIDIA, AMD, and Intel",
        })),
      };
    } else if (
      interaction.type === "plan_review" ||
      interaction.type === "outline_review"
    ) {
      return { approved: true };
    } else if (interaction.type === "source_review") {
      return { included_domains: [], excluded_domains: [] };
    }

    return null; // Don't respond — continue polling
  },
});

console.log(result.output);
```

**Parameters:**

| Parameter       | Type                                                                                                | Description                                                                                      |
| --------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `onInteraction` | `(interaction: Interaction) => Promise<Record<string, any> \| null> \| Record<string, any> \| null` | Called when a HITL checkpoint fires. Return a response object to auto-respond, or `null` to skip |

## Convenience helpers

Type-safe methods for responding to each checkpoint type:

### `respondPlanningQuestions()`

```typescript theme={null}
await client.deepresearch.respondPlanningQuestions(
  taskId,
  interaction.interaction_id,
  [
    { question: "What regions?", answer: "North America and EU" },
    { question: "Specific competitors?", answer: "NVIDIA, AMD, Intel" },
  ]
);
```

### `approvePlan()`

```typescript theme={null}
// Approve
await client.deepresearch.approvePlan(taskId, interaction.interaction_id);

// Request modifications
await client.deepresearch.approvePlan(
  taskId,
  interaction.interaction_id,
  "Focus more on supply chain analysis"
);
```

### `respondSourceReview()`

```typescript theme={null}
await client.deepresearch.respondSourceReview(
  taskId,
  interaction.interaction_id,
  {
    includedDomains: ["sec.gov", "plos.org"],
    excludedDomains: ["example.com"],
  }
);
```

### `approveOutline()`

```typescript theme={null}
// Approve
await client.deepresearch.approveOutline(taskId, interaction.interaction_id);

// Request modifications
await client.deepresearch.approveOutline(
  taskId,
  interaction.interaction_id,
  "Add a regulatory risks section"
);
```

## Complete example

```typescript theme={null}
import Valyu from "valyu-js";

const client = new Valyu();

async function runWithHitl() {
  // Create task with selective checkpoints
  const task = await client.deepresearch.create({
    query: "Due diligence report on renewable energy company SunPower",
    mode: "heavy",
    hitl: {
      planReview: true,
      sourceReview: true,
    },
  });

  const taskId = task.deepresearch_id!;
  console.log(`Task: ${taskId}`);

  while (true) {
    const status = await client.deepresearch.status(taskId);
    console.log(`Status: ${status.status}`);

    if (
      (status.status === "awaiting_input" || status.status === "paused") &&
      status.interaction
    ) {
      const { type, data, interaction_id } = status.interaction;

      if (type === "plan_review") {
        console.log(`\nResearch plan:\n${data.plan}`);
        console.log(`Areas: ${data.research_areas?.join(", ")}`);

        // Auto-approve with a tweak
        await client.deepresearch.respond(taskId, interaction_id, {
          approved: false,
          modifications: "Include analysis of their patent portfolio",
        });
      } else if (type === "source_review") {
        console.log(`\nFound ${data.total_sources} sources:`);
        for (const domain of data.domains) {
          console.log(
            `  ${domain.domain}: ${domain.source_count} sources (AI: ${domain.ai_recommendation})`
          );
        }

        // Accept AI recommendations
        await client.deepresearch.respond(taskId, interaction_id, {
          included_domains: [],
          excluded_domains: [],
        });
      }
    } else if (status.status === "completed") {
      console.log(`\nReport complete! Cost: $${status.cost}`);
      console.log(String(status.output).substring(0, 500));
      break;
    } else if (status.status === "failed" || status.status === "cancelled") {
      console.log(`Task ${status.status}: ${status.error}`);
      break;
    }

    await new Promise((r) => setTimeout(r, 5000));
  }
}

runWithHitl();
```

## HITL history

After checkpoints complete, the `hitl_history` field on the status response contains a record of each interaction:

```typescript theme={null}
const status = await client.deepresearch.status(taskId);

if (status.hitl_history) {
  for (const entry of status.hitl_history) {
    console.log(`Checkpoint: ${entry.type}`);
    console.log(`  Auto-continued: ${entry.auto_continued}`);
    if (entry.responded_at) {
      const responseTime = (entry.responded_at - entry.created_at) / 1000;
      console.log(`  Response time: ${responseTime.toFixed(1)}s`);
    }
  }
}
```

## Types reference

| Type                          | Description                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------------------------- |
| `HitlConfig`                  | Configuration with 4 optional boolean fields                                                |
| `InteractionType`             | Union: `"planning_questions"` \| `"plan_review"` \| `"source_review"` \| `"outline_review"` |
| `Interaction`                 | Checkpoint payload with `interaction_id`, `type`, `data`, `created_at`, `timeout_ms`        |
| `InteractionHistoryEntry`     | Completed checkpoint record with `auto_continued`, `responded_at`, `response`               |
| `DeepResearchRespondResponse` | Response from `respond()` with `success`, `status`, `deepresearch_id`                       |
