Better Agent
Use @blyp/core/ai/better-agent when your app uses Better Agent and you want Blyp to emit one normalized run-level ai_trace record per Better Agent run.
This integration has two surfaces:
blypPlugin(options?)for app-level Better Agent plugin registrationcreateBetterAgentTracker(options?)for manual wiring when plugin registration is not possible
Better Agent runs can span multiple model calls and tool steps. Blyp aggregates those steps into one final trace for the run. It does not emit one trace per model step.
Install
Install Blyp and Better Agent in the app that runs the traced Better Agent runtime:
bun add @blyp/core @better-agent/coreBetter Agent support lives in the @blyp/core/ai/better-agent entrypoint.
@better-agent/core is an optional peer dependency of @blyp/core, but it is required when you use this integration.
Quick start
Register the Blyp plugin in the Better Agent app plugins array:
import { betterAgent } from "@better-agent/core";
import { blypPlugin } from "@blyp/core/ai/better-agent";
declare const supportAgent: unknown;
export const app = betterAgent({
agents: [supportAgent],
plugins: [
blypPlugin({
operation: "support_chat",
metadata: {
service: "api",
team: "support",
},
capture: {
input: true,
output: true,
toolInputs: true,
toolOutputs: true,
},
}),
],
});With that plugin installed, Better Agent run events and model responses are aggregated into one Blyp ai_trace with provider, model, operation, timing, usage, and optional captured content.
Public API
@blyp/core/ai/better-agent exports:
import {
blypPlugin,
createBetterAgentTracker,
type BlypBetterAgentOptions,
type BlypBetterAgentRunResolver,
type BlypBetterAgentTracker,
} from "@blyp/core/ai/better-agent";blypPlugin(options?)
import type { Plugin } from "@better-agent/core";
import type { BlypBetterAgentOptions } from "@blyp/core/ai/better-agent";
declare function blypPlugin(options?: BlypBetterAgentOptions): Plugin;Use this when you can register a Better Agent plugin at the app level. This is the preferred path because the Better Agent runtime will call both onEvent and onAfterModelCall for you.
createBetterAgentTracker(options?)
import type { BlypBetterAgentOptions, BlypBetterAgentTracker } from "@blyp/core/ai/better-agent";
declare function createBetterAgentTracker(
options?: BlypBetterAgentOptions
): BlypBetterAgentTracker;Use this when plugin registration is not possible and you need to forward Better Agent events and model responses manually.
BlypBetterAgentTracker
import type { Event } from "@better-agent/core/events";
import type { GenerativeModelResponse } from "@better-agent/core/providers";
type BlypBetterAgentTracker = {
onEvent(event: Event): Promise<void>;
onAfterModelCall(
response: GenerativeModelResponse,
info?: { stepIndex?: number }
): Promise<void>;
};One tracker instance handles one Better Agent run.
BlypBetterAgentOptions
BlypBetterAgentOptions builds on Blyp’s shared AI tracing options. It inherits capture, exclude, limits, and hooks from Blyp’s provider-level tracing config, then adds Better Agent-specific run resolution.
Conceptually:
type BlypBetterAgentOptions =
Omit<BlypProviderOptions, "provider" | "operation" | "metadata"> & {
provider?: string;
operation?: string;
metadata?: Record<string, unknown>;
resolveRun?: BlypBetterAgentRunResolver;
};BlypBetterAgentRunResolver
type BlypBetterAgentRunResolver = (ctx: {
runId: string;
agentName: string;
conversationId?: string;
}) =>
| {
provider?: string;
model?: string;
operation?: string;
method?: string;
metadata?: Record<string, unknown>;
streamed?: boolean;
}
| Promise<
| {
provider?: string;
model?: string;
operation?: string;
method?: string;
metadata?: Record<string, unknown>;
streamed?: boolean;
}
| undefined
>
| undefined;The resolver runs once at RUN_STARTED time and lets you override provider/model/operation metadata for that run.
Option reference
Better Agent-specific top-level options
logger: optional Blyp logger override. If omitted, Blyp uses the active request-scoped logger when available, then falls back to the root logger.provider: default provider label for the run whenresolveRun()does not override it.operation: default operation name for the run whenresolveRun()does not override it.metadata: base metadata merged into the final trace.resolveRun: Better Agent-specific resolver that can derive provider/model/operation metadata fromrunId,agentName, andconversationId.
model and method are not top-level BlypBetterAgentOptions fields. If you need to override them, do it in resolveRun().
resolveRun() input
resolveRun() receives:
runIdagentNameconversationId?
It can return overrides for:
providermodeloperationmethodmetadatastreamed
Resolver metadata is merged on top of top-level metadata. Blyp then appends built-in run metadata such as agentName, runId, conversationId, and final stepCount.
Inherited Blyp AI tracing options
The Better Agent integration inherits these Blyp AI tracing options:
captureexcludelimitshooks
capture
Supported capture flags:
capture.inputcapture.outputcapture.toolInputscapture.toolOutputscapture.reasoningcapture.streamEventscapture.streamChunkscapture.rawProviderPayload
streamEvents and streamChunks are normalized together by Blyp’s shared AI tracing config. Enabling either enables the same ai.chunk event capture path.
exclude
Supported exclude flags:
exclude.providerOptionsexclude.requestPathsexclude.responsePathsexclude.metadataPathsexclude.toolNames
exclude.providerOptions is inherited from shared Blyp AI tracing, but it has no practical effect for Better Agent traces because this integration does not serialize providerOptions.
limits
Supported limits:
limits.maxContentByteslimits.maxEventslimits.maxToolCalls
hooks
Supported hooks:
hooks.onStarthooks.onFinishhooks.onErrorhooks.onEvent
These are Blyp tracing hooks, not Better Agent runtime hooks. For example, hooks.onEvent receives normalized Blyp trace events such as ai.start, ai.chunk, and ai.finish.
Behavior and trace model
The Better Agent integration emits one normalized Blyp ai_trace per Better Agent run.
Key behavior:
- Blyp initializes trace state on
RUN_STARTED - Blyp finalizes the trace on
RUN_FINISHED,RUN_ERROR, orRUN_ABORTED - Better Agent multi-step runs are aggregated into one final trace
- usage is aggregated across every
onAfterModelCall()invocation during the run - tool activity is aggregated from Better Agent runtime events
- final output comes from the last Better Agent response, with streamed text and reasoning used as fallback if needed
RUN_ABORTEDforcesfinishReasontoabortRUN_ERRORemits the trace at error level- plugin failures are fail-open because the Better Agent runtime catches plugin
onEventandonAfterModelCallerrors, and Blyp also guards its own hooks and log emission
Conceptually, the final record looks like:
type = "ai_trace"ai.sdk = "better-agent-sdk"ai.providerai.modelai.operationai.methodai.metadataai.inputai.outputai.reasoningai.toolCallsai.usageai.timing
Defaults
Default behavior is intentionally conservative:
methoddefaults to"agent.run"operationdefaults toagentNameproviderdefaults to"better-agent"unless overriddenmodeldefaults toagentNameunless overriddenstreamedstarts asfalse, then becomestrueifresolveRun()sets it or if Blyp sees live runtime events such as text, reasoning, tool, or data-part streaming events- input, output, reasoning, tool payloads, stream events, and raw provider payloads are not captured unless you enable them
- request-scoped Blyp logger inheritance works the same way as other Blyp AI integrations
Resolver example
Use resolveRun() when you want Better Agent run traces to carry richer provider/model identity than the Better Agent plugin hook context exposes directly:
import { blypPlugin } from "@blyp/core/ai/better-agent";
const plugin = blypPlugin({
resolveRun({ agentName, runId, conversationId }) {
if (agentName === "support-agent") {
return {
provider: "openai",
model: "gpt-5",
operation: "support_chat",
metadata: {
runId,
conversationId,
route: "support",
},
};
}
return {
provider: "better-agent",
model: agentName,
operation: `better-agent:${agentName}`,
};
},
});
void plugin;Why this matters:
- Better Agent’s plugin hook surface gives Blyp the run identity and final model responses
- it does not directly provide a normalized provider/model mapping for the underlying provider used by the agent
resolveRun()is where you enrich the trace with that provider/model identity
Manual tracker
createBetterAgentTracker() is the fallback path when you cannot register blypPlugin() at the Better Agent app level.
Rules for manual use:
- create one tracker instance per run
- forward Better Agent runtime events with
tracker.onEvent(event) - forward every model response with
tracker.onAfterModelCall(response, { stepIndex }) - let terminal Better Agent events finalize the trace
Example:
import type { RunFinishedEvent, RunStartedEvent } from "@better-agent/core/events";
import type { GenerativeModelResponse } from "@better-agent/core/providers";
import { createBetterAgentTracker } from "@blyp/core/ai/better-agent";
const tracker = createBetterAgentTracker({
capture: {
output: true,
rawProviderPayload: true,
},
});
const runStartedEvent: RunStartedEvent = {
type: "RUN_STARTED",
timestamp: Date.now(),
runId: "run_1",
agentName: "support-agent",
conversationId: "conv_1",
runInput: { input: "Where is my order?" },
};
const modelResponse: GenerativeModelResponse = {
output: [
{
type: "message",
role: "assistant",
content: [{ type: "text", text: "I found your order." }],
},
],
finishReason: "stop",
usage: {
inputTokens: 10,
outputTokens: 6,
totalTokens: 16,
},
request: {
body: { step: 0 },
},
response: {
body: { id: "resp_1" },
},
};
const runFinishedEvent: RunFinishedEvent = {
type: "RUN_FINISHED",
timestamp: Date.now(),
runId: "run_1",
agentName: "support-agent",
conversationId: "conv_1",
result: {
response: modelResponse,
},
};
await tracker.onEvent(runStartedEvent);
await tracker.onAfterModelCall(modelResponse, { stepIndex: 0 });
await tracker.onEvent(runFinishedEvent);Important limitation:
Without onAfterModelCall(), the tracker can still emit a trace from runtime events and the terminal Better Agent response, but aggregated usage and raw provider payload capture will be incomplete. In that case Blyp falls back to final-response-only usage and payload fidelity.
Event mapping
This is how Better Agent runtime events map into Blyp tracing:
RUN_STARTED: initializes trace state and captures run inputSTEP_START: contributes to step accountingSTEP_FINISH: contributes to step accountingRUN_FINISHED: finalizes a successful trace and captures final output from the final Better Agent responseRUN_ERROR: finalizes an error traceRUN_ABORTED: finalizes the trace withfinishReason = "abort"TEXT_MESSAGE_CONTENT: contributes streamed output and can mark first live chunk timingREASONING_MESSAGE_CONTENT: contributes reasoning capture when enabled and can also mark first live chunk timingTOOL_CALL_START: starts a tool call recordTOOL_CALL_ARGS: accumulates tool inputTOOL_CALL_RESULT: stores tool output or tool errorDATA_PART: contributes structured stream chunk data when stream event capture is enabled
Privacy and capture
Better Agent tracing uses the same Blyp privacy model as the other AI integrations.
Important points:
- input, output, tool payloads, reasoning, and raw provider payloads are not captured unless you enable them
exclude.metadataPaths,exclude.requestPaths,exclude.responsePaths, andexclude.toolNamesare applied to the final structured trace- truncation follows Blyp AI
limits - stream event capture can materially increase payload volume
For the shared privacy controls, see AI Privacy & Capture.
Limitations
- plugin registration with
blypPlugin()is the preferred path; the manual tracker is a fallback - provider and model default to
better-agentandagentNameunless you override them withresolveRun() - manual tracker usage without
onAfterModelCall()reduces fidelity for aggregated usage and raw provider payload capture - Better Agent traces are run-level summaries, not per-step traces
- Blyp can only capture Better Agent events and model responses that the runtime actually surfaces
Comparison to other Blyp AI integrations
The Better Agent integration sits at a different layer than Blyp’s other AI entrypoints:
@blyp/core/ai/vercelinstruments Vercel AI SDK middleware and wrapped models@blyp/core/ai/openaiwraps the OpenAI SDK client directly@blyp/core/ai/anthropicwraps the Anthropic SDK client directly@blyp/core/ai/better-agentintegrates at the Better Agent runtime/plugin layer
That difference matters for trace shape. Better Agent emits one run-level summary trace for the whole agent run, not one provider-call trace per step.
Additional examples
Capture-heavy debugging
Use aggressive capture when you need to inspect the full run shape during debugging:
import { blypPlugin } from "@blyp/core/ai/better-agent";
const plugin = blypPlugin({
operation: "support_debug",
capture: {
input: true,
output: true,
reasoning: true,
toolInputs: true,
toolOutputs: true,
streamEvents: true,
rawProviderPayload: true,
},
limits: {
maxContentBytes: 32_768,
maxEvents: 500,
maxToolCalls: 100,
},
});
void plugin;Conservative production capture
Use a resolver and minimal capture when you want low-risk production tracing:
import { blypPlugin } from "@blyp/core/ai/better-agent";
const plugin = blypPlugin({
resolveRun({ agentName }) {
return {
provider: "openai",
model: agentName === "support-agent" ? "gpt-5" : agentName,
operation: `agent:${agentName}`,
};
},
capture: {
input: false,
output: false,
toolInputs: false,
toolOutputs: false,
reasoning: false,
rawProviderPayload: false,
},
exclude: {
metadataPaths: ["tenant.secret"],
toolNames: ["internalAdminTool"],
},
});
void plugin;