diff --git a/api/server/services/Endpoints/agents/initialize.spec.js b/api/server/services/Endpoints/agents/initialize.spec.js index 927291b6b1..66335332ef 100644 --- a/api/server/services/Endpoints/agents/initialize.spec.js +++ b/api/server/services/Endpoints/agents/initialize.spec.js @@ -49,6 +49,7 @@ const mockArtifactToolEndCallback = jest.fn(); jest.mock('~/server/controllers/agents/callbacks', () => ({ createToolEndCallback: jest.fn(() => mockArtifactToolEndCallback), createAttachmentEmitter: jest.fn(() => jest.fn()), + createPtcProgressEmitter: jest.fn(() => jest.fn()), createBackgroundCodeResultHandler: jest.fn(() => jest.fn()), getDefaultHandlers: jest.fn((opts) => { capturedDefaultHandlerOptions = opts; @@ -206,6 +207,7 @@ describe('initializeClient — processAgent ACL gate', () => { it('threads the owning job epoch into resumable event handlers', async () => { const { createAttachmentEmitter, + createPtcProgressEmitter, createToolEndCallback, } = require('~/server/controllers/agents/callbacks'); mockInitializeAgent.mockResolvedValue(makePrimaryConfig([])); @@ -237,6 +239,13 @@ describe('initializeClient — processAgent ACL gate', () => { streamId: 'conv_1', jobCreatedAt: 1234, }); + /** The PTC trace emitter is generation-fenced like every other resumable + * emitter; a stale epoch would leak one run's inner calls into the next. */ + expect(createPtcProgressEmitter).toHaveBeenCalledWith({ + res: {}, + streamId: 'conv_1', + jobCreatedAt: 1234, + }); mockLoadToolsForExecution.mockResolvedValue({ loadedTools: [], configurable: {} }); await capturedToolExecuteOptions.loadTools([], PRIMARY_ID); diff --git a/client/src/components/Chat/Messages/Content/Parts/PtcToolTrace.tsx b/client/src/components/Chat/Messages/Content/Parts/PtcToolTrace.tsx index 4567bd984a..50ad9273a2 100644 --- a/client/src/components/Chat/Messages/Content/Parts/PtcToolTrace.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/PtcToolTrace.tsx @@ -4,8 +4,8 @@ import { isReportableRunStepDuration } from 'librechat-data-provider'; import type { TranslationKeys } from '~/hooks'; import type { PtcTraceEntry } from '~/store'; import { cn, parseToolName, getRunStepDurationLabels } from '~/utils'; -import { ptcTraceByToolCallId, ptcTraceKey } from '~/store'; import { useMessageContext } from '~/Providers/MessageContext'; +import { ptcTraceByToolCallId, ptcTraceKey } from '~/store'; import { useMCPServerNames } from '~/hooks/MCP'; import { useLocalize } from '~/hooks'; diff --git a/packages/api/src/agents/handlers.ts b/packages/api/src/agents/handlers.ts index f7c89e20bc..00d440aab6 100644 --- a/packages/api/src/agents/handlers.ts +++ b/packages/api/src/agents/handlers.ts @@ -14,8 +14,8 @@ import type { SubagentTaskConfig, } from '@librechat/agents'; import type { StructuredToolInterface } from '@librechat/agents/langchain/tools'; -import type { ValidationIssue } from '@librechat/data-schemas'; import type { CodeEnvRef, PtcToolCallEvent } from 'librechat-data-provider'; +import type { ValidationIssue } from '@librechat/data-schemas'; import type { SkillFileRecord, PrimeSkillFilesResult } from './skillFiles'; import type { CodeExecutionContext } from './execution'; import type { TextContentFragment } from '~/protection';