Skip to main content
By default, evals, enrichments, and actions run at the session level. Use the scope option to target subagent logs individually.

Scope options

ScopeRuns at session levelRuns at subagent level
'session' (default)YesNo
'subagent'NoYes
'both'YesYes
// Session only (default)
app.eval('turn-count', ({ stats }) => ({
  pass: stats.turnCount <= 50,
  score: Math.max(0, 1 - stats.turnCount / 100),
}));

// Subagent only
app.eval('agent-efficiency', ({ stats }) => ({
  pass: stats.turnCount <= 10,
  score: Math.max(0, 1 - stats.turnCount / 20),
  message: `${stats.turnCount} turns`,
}), { scope: 'subagent' });

// Both
app.eval('quality-check', ({ stats, source }) => ({
  pass: stats.toolCallCount <= 20,
  score: Math.max(0, 1 - stats.toolCallCount / 40),
  message: `${source}: ${stats.toolCallCount} tool calls`,
}), { scope: 'both' });

Subagent context fields

When running at subagent level, the context includes additional fields:
FieldTypeDescription
sourcestring"agent-{id}" - matches entry._source directly
subagentTypestringe.g. 'Explore', 'Bash'
subagentDescriptionstringShort description of what the subagent was doing
parentSessionIdstringThe parent session’s ID
app.eval('adaptive-eval', (ctx) => {
  if (ctx.source !== 'session') {
    console.log(ctx.source);              // e.g. 'agent-a1b2c3'
    console.log(ctx.subagentType);        // e.g. 'Explore'
    console.log(ctx.subagentDescription); // e.g. 'Search for auth code'
    console.log(ctx.parentSessionId);
  }
  return { pass: true };
}, { scope: 'both' });

Filtering entries by source

Subagent-scoped evals receive the full combined data (session + all subagents), not just the subagent’s own entries. Use _source to filter to only the current subagent’s entries:
app.eval('explore-thoroughness', ({ entries, source }) => {
  const myEntries = entries.filter(e => e._source === source);
  return {
    pass: myEntries.length > 5,
    score: Math.min(myEntries.length / 20, 1),
    message: `${myEntries.length} entries`,
  };
}, { scope: 'subagent', subagentType: 'Explore' });
source in the context directly matches the _source value on entries, so the filter is always exact.

subagentType filtering

When you specify subagentType, the eval only runs for subagents of that type. Subagents of other types won’t see the eval panel at all.
// Only runs for Explore subagents
app.eval('explore-depth', ({ entries, source }) => {
  const myEntries = entries.filter(e => e._source === source);
  return {
    pass: myEntries.length > 5,
    score: Math.min(myEntries.length / 20, 1),
  };
}, { scope: 'subagent', subagentType: 'Explore' });

// Runs for all subagent types
app.eval('agent-turn-count', ({ stats }) => ({
  pass: stats.turnCount <= 10,
  score: Math.max(0, 1 - stats.turnCount / 20),
}), { scope: 'subagent' });

Subagent enrichments and actions

The same scope and subagentType options apply to enrichments and actions:
app.enrich('agent-summary',
  ({ stats, entries, source }) => ({
    'Agent Turns': stats.turnCount,
    'Agent Tool Calls': stats.toolCallCount,
    'Agent Entries': entries.filter(e => e._source === source).length,
  }),
  { scope: 'subagent' }
);

app.action('agent-report', ({ entries, source, stats }) => {
  const myEntries = entries.filter(e => e._source === source);
  return {
    output: [
      `Source: ${source}`,
      `Entries: ${myEntries.length}`,
      `Turns: ${stats.turnCount}`,
    ].join('\n'),
    status: 'success',
  };
}, { scope: 'subagent' });