diff --git a/packages/junior/src/chat/logging.ts b/packages/junior/src/chat/logging.ts index 1b131e141..3119ee3b6 100644 --- a/packages/junior/src/chat/logging.ts +++ b/packages/junior/src/chat/logging.ts @@ -141,6 +141,7 @@ function normalizeGenAiFinishReasons(value: unknown): unknown { } const contextStorage = new AsyncLocalStorage(); +const spanStorage = new AsyncLocalStorage(); const logRecordSinks = new Set<(record: EmittedLogRecord) => void>(); const deploymentLogAttributes = getDeploymentTelemetryAttributes(); type ConsoleTextStyle = Parameters[0]; @@ -1664,7 +1665,7 @@ export async function withSpan( ...normalizedAttributes, }, }, - callback, + (span) => spanStorage.run(span, callback), ); }); } @@ -1694,7 +1695,7 @@ export function getTracePropagationHeaders(): TracePropagationHeaders { /** Set attributes on the currently active Sentry span. */ export function setSpanAttributes(attributes: Record): void { const sentry = Sentry as unknown as { getActiveSpan?: () => unknown }; - const span = sentry.getActiveSpan?.(); + const span = sentry.getActiveSpan?.() ?? spanStorage.getStore(); if (!span) { return; } @@ -1716,7 +1717,7 @@ export function setSpanAttributes(attributes: Record): void { /** Set the status of the currently active Sentry span. */ export function setSpanStatus(status: "ok" | "error"): void { const sentry = Sentry as unknown as { getActiveSpan?: () => unknown }; - const span = sentry.getActiveSpan?.(); + const span = sentry.getActiveSpan?.() ?? spanStorage.getStore(); if (!span) { return; } diff --git a/packages/junior/src/chat/tool-support/pi-tool-adapter.ts b/packages/junior/src/chat/tool-support/pi-tool-adapter.ts index 751ee76d5..da628d5cf 100644 --- a/packages/junior/src/chat/tool-support/pi-tool-adapter.ts +++ b/packages/junior/src/chat/tool-support/pi-tool-adapter.ts @@ -112,6 +112,40 @@ export function createPiAgentTools( } return true; }; + const isRecord = (value: unknown): value is Record => + Boolean(value && typeof value === "object" && !Array.isArray(value)); + const contentEchoesDetails = ( + content: ReturnType["content"], + details: unknown, + ): boolean => { + if (content.length !== 1 || content[0]?.type !== "text") { + return false; + } + try { + return ( + JSON.stringify(JSON.parse(content[0].text)) === JSON.stringify(details) + ); + } catch { + return false; + } + }; + const toolResultTracePayload = ( + normalized: ReturnType, + ) => { + const { content, details } = normalized; + if (isRecord(details) && details.rawResult !== undefined) { + return details.rawResult; + } + if ( + content.length > 0 && + isRecord(details) && + details.content === undefined && + !contentEchoesDetails(content, details) + ) { + return { ...details, content }; + } + return details; + }; const executeDefinition = async (args: { normalizedToolCallId: string | undefined; params: Record; @@ -154,16 +188,7 @@ export function createPiAgentTools( if (isSandbox && pluginAuthOrchestration) { await pluginAuthOrchestration.maybeHandleAuthSignal(normalized.details); } - let resultAttributeValue = normalized.details; - if ( - normalized.details && - typeof normalized.details === "object" && - "rawResult" in normalized.details && - (normalized.details as { rawResult?: unknown }).rawResult !== undefined - ) { - resultAttributeValue = (normalized.details as { rawResult: unknown }) - .rawResult; - } + const resultAttributeValue = toolResultTracePayload(normalized); const toolResultAttribute = serializeToolPayload(resultAttributeValue); if (toolResultAttribute) { setSpanAttributes({