Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions src/agents/productionAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,15 @@ import {
} from '../types';
import { MaybeMock } from '../maybe-mock';
import {
appendTranscriptToHistory,
writeMetaFileToHistory,
updateMetadataEndTime,
writeTraceToHistory,
requestWithEndpointFallback,
getHistoryDir,
getAllHistory,
TranscriptEntry,
logSessionToIndex,
getAgentIndexDir,
logTurnToHistory,
recordTraceForTurn,
SessionHistoryBuffer,
} from '../utils';
import { createTraceFlag, findTraceFlag, getDebugLog } from '../apexUtils';
import { AgentBase } from './agentBase';
Expand All @@ -51,6 +50,8 @@ export class ProductionAgent extends AgentBase {
private botMetadata: BotMetadata | undefined;
private id: string | undefined;
private apiName: string | undefined;
private turnCounter = 0;
private historyBuffer: SessionHistoryBuffer | undefined;
private readonly apiBase: string;

public constructor(private options: ProductionAgentOptions) {
Expand Down Expand Up @@ -220,6 +221,9 @@ export class ProductionAgent extends AgentBase {
if (!this.sessionId) {
throw SfError.create({ name: 'noSessionId', message: 'Agent not started, please call .start() first' });
}
if (!this.historyBuffer) {
throw SfError.create({ name: 'noHistoryBuffer', message: 'Session not initialized properly' });
}

const url = `${this.apiBase}/sessions/${this.sessionId}/messages`;

Expand Down Expand Up @@ -247,16 +251,14 @@ export class ProductionAgent extends AgentBase {
this.historyDir = await getHistoryDir(agentId, this.sessionId);
}

void appendTranscriptToHistory(
{
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'user',
text: message,
},
this.historyDir
);
const userEntry = {
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'user' as const,
text: message,
};
await logTurnToHistory(userEntry, ++this.turnCounter, this.historyDir, this.historyBuffer);

const response = await requestWithEndpointFallback<AgentPreviewSendResponse>(this.connection, {
method: 'POST',
Expand All @@ -270,24 +272,25 @@ export class ProductionAgent extends AgentBase {
const planId = response.messages.at(0)!.planId;
this.planIds.add(planId);

await appendTranscriptToHistory(
{
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'agent',
text: response.messages.at(0)?.message,
raw: response.messages,
},
this.historyDir
);
const agentEntry = {
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'agent' as const,
text: response.messages.at(0)?.message,
raw: response.messages,
};
const agentTurn = ++this.turnCounter;
await logTurnToHistory(agentEntry, agentTurn, this.historyDir, this.historyBuffer);

// Fetch and write trace immediately if available
if (planId) {
const trace = await this.getTrace(planId);
await writeTraceToHistory(planId, trace, this.historyDir);
await recordTraceForTurn(this.historyDir, agentTurn, planId, undefined, this.historyBuffer);
}

// Flush buffer to keep turn-index.json and metadata.json up to date
await this.historyBuffer.flush();

if (this.apexDebugging && this.canApexDebug()) {
const apexLog = await getDebugLog(this.connection, start, Date.now());
if (apexLog) {
Expand Down Expand Up @@ -367,26 +370,22 @@ export class ProductionAgent extends AgentBase {
this.historyDir = await getHistoryDir(agentId, response.sessionId);
const startTime = new Date().toISOString();

await appendTranscriptToHistory(
{
timestamp: startTime,
agentId,
sessionId: response.sessionId,
role: 'agent',
text: response.messages.map((m) => m.message).join('\n'),
raw: response.messages,
},
this.historyDir
);
// Initialize history buffer (no file I/O yet)
this.historyBuffer = new SessionHistoryBuffer(this.historyDir, response.sessionId, agentId, startTime);
this.turnCounter = 0;

// Write initial metadata
await writeMetaFileToHistory(this.historyDir, {
sessionId: response.sessionId,
const initialEntry = {
timestamp: startTime,
agentId,
startTime,
apexDebugging: this.apexDebugging,
planIds: [],
});
sessionId: response.sessionId,
role: 'agent' as const,
text: response.messages.map((m) => m.message).join('\n'),
raw: response.messages,
};
await logTurnToHistory(initialEntry, ++this.turnCounter, this.historyDir, this.historyBuffer);

// Write turn-index.json and metadata.json immediately so they exist after session start
await this.historyBuffer.flush();

const agentDir = await getAgentIndexDir(agentId);
await logSessionToIndex(agentDir, {
Expand Down Expand Up @@ -427,26 +426,27 @@ export class ProductionAgent extends AgentBase {
},
});

// Write end entry immediately
if (this.historyDir) {
await appendTranscriptToHistory(
{
timestamp: new Date().toISOString(),
agentId: this.id,
sessionId: this.sessionId,
role: 'agent',
reason,
raw: response.messages,
},
this.historyDir
);
// Update metadata with end time
await updateMetadataEndTime(this.historyDir, new Date().toISOString(), this.planIds);
// Write end entry and flush buffer
if (this.historyDir && this.historyBuffer) {
const endTime = new Date().toISOString();
const endEntry = {
timestamp: endTime,
agentId: this.id,
sessionId: this.sessionId,
role: 'agent' as const,
reason,
raw: response.messages,
};
await logTurnToHistory(endEntry, ++this.turnCounter, this.historyDir, this.historyBuffer);

// Flush all buffered data to disk (turn-index.json and metadata.json)
await this.historyBuffer.flush(endTime);
}

// Clear session data for next session
this.sessionId = undefined;
this.historyDir = undefined;
this.historyBuffer = undefined;
this.planIds = new Set<string>();

return response;
Expand Down
102 changes: 58 additions & 44 deletions src/agents/scriptAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,17 @@ import {
ScriptAgentOptions,
} from '../types';
import {
appendTranscriptToHistory,
writeMetaFileToHistory,
logSessionToIndex,
updateMetadataEndTime,
writeTraceToHistory,
getHttpStatusCode,
requestWithEndpointFallback,
findAuthoringBundle,
getHistoryDir,
TranscriptEntry,
getAllHistory,
getAgentIndexDir,
logTurnToHistory,
recordTraceForTurn,
SessionHistoryBuffer,
} from '../utils';
import { getDebugLog } from '../apexUtils';
import { generateAgentScript } from '../templates/agentScriptTemplate';
Expand All @@ -58,6 +57,8 @@ export class ScriptAgent extends AgentBase {
setMockMode: (mockMode: 'Mock' | 'Live Test') => void;
};
private mockMode: 'Mock' | 'Live Test' = 'Mock';
private turnCounter = 0;
private historyBuffer: SessionHistoryBuffer | undefined;
private agentScriptContent: AgentScriptContent;
private agentJson: AgentJson | undefined;
private readonly apiBase: string;
Expand Down Expand Up @@ -296,25 +297,26 @@ export class ScriptAgent extends AgentBase {
return Promise.resolve({ messages: [], _links: [] } as unknown as AgentPreviewEndResponse);
}

if (this.historyDir) {
await appendTranscriptToHistory(
{
timestamp: new Date().toISOString(),
agentId: this.getAgentIdForStorage(),
sessionId: this.sessionId,
role: 'agent',
reason: 'UserRequest',
raw: [],
},
this.historyDir
);
// Update metadata with end time
await updateMetadataEndTime(this.historyDir, new Date().toISOString(), this.planIds);
if (this.historyDir && this.historyBuffer) {
const endTime = new Date().toISOString();
const endEntry = {
timestamp: endTime,
agentId: this.getAgentIdForStorage(),
sessionId: this.sessionId,
role: 'agent' as const,
reason: 'UserRequest',
raw: [],
};
await logTurnToHistory(endEntry, ++this.turnCounter, this.historyDir, this.historyBuffer);

// Flush all buffered data to disk (turn-index.json and metadata.json)
await this.historyBuffer.flush(endTime);
}

// Clear session data for next session
this.sessionId = undefined;
this.historyDir = undefined;
this.historyBuffer = undefined;
this.planIds = new Set<string>();

return Promise.resolve({ messages: [], _links: [] } as unknown as AgentPreviewEndResponse);
Expand All @@ -340,6 +342,9 @@ export class ScriptAgent extends AgentBase {
if (!this.sessionId) {
throw SfError.create({ name: 'noSessionId', message: 'Agent not started, please call .start() first' });
}
if (!this.historyBuffer) {
throw SfError.create({ name: 'noHistoryBuffer', message: 'Session not initialized properly' });
}

const url = `${this.apiBase}/v1.1/preview/sessions/${this.sessionId}/messages`;

Expand Down Expand Up @@ -367,15 +372,17 @@ export class ScriptAgent extends AgentBase {
this.historyDir = await getHistoryDir(agentId, this.sessionId);
}

void appendTranscriptToHistory(
await logTurnToHistory(
{
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'user',
role: 'user' as const,
text: message,
},
this.historyDir
++this.turnCounter,
this.historyDir,
this.historyBuffer
);

const response = await requestWithEndpointFallback<AgentPreviewSendResponse>(this.connection, {
Expand All @@ -390,24 +397,30 @@ export class ScriptAgent extends AgentBase {
const planId = response.messages.at(0)!.planId;
this.planIds.add(planId);

await appendTranscriptToHistory(
const agentTurn = ++this.turnCounter;
await logTurnToHistory(
{
timestamp: new Date().toISOString(),
agentId,
sessionId: this.sessionId,
role: 'agent',
role: 'agent' as const,
text: response.messages.at(0)?.message,
raw: response.messages,
},
this.historyDir
agentTurn,
this.historyDir,
this.historyBuffer
);

// Fetch and write trace immediately if available
if (planId) {
const trace = await this.getTrace(planId);
await writeTraceToHistory(planId, trace, this.historyDir);
await recordTraceForTurn(this.historyDir, agentTurn, planId, trace, this.historyBuffer);
}

// Flush buffer to keep turn-index.json and metadata.json up to date
await this.historyBuffer.flush();

if (this.apexDebugging && this.canApexDebug()) {
const apexLog = await getDebugLog(this.connection, start, Date.now());
if (apexLog) {
Expand Down Expand Up @@ -522,28 +535,29 @@ export class ScriptAgent extends AgentBase {
this.historyDir = await getHistoryDir(agentIdForStorage, response.sessionId);
const startTime = new Date().toISOString();

// Write initial agent messages immediately
await appendTranscriptToHistory(
{
timestamp: startTime,
agentId: agentIdForStorage,
sessionId: response.sessionId,
role: 'agent',
text: response.messages.map((m) => m.message).join('\n'),
raw: response.messages,
},
this.historyDir
// Initialize history buffer (no file I/O yet)
this.historyBuffer = new SessionHistoryBuffer(
this.historyDir,
response.sessionId,
agentIdForStorage,
startTime,
this.mockMode
);
this.turnCounter = 0;

// Write initial metadata
await writeMetaFileToHistory(this.historyDir, {
sessionId: response.sessionId,
// Write initial agent messages immediately
const initialEntry = {
timestamp: startTime,
agentId: agentIdForStorage,
startTime,
apexDebugging: this.apexDebugging,
mockMode: this.mockMode,
planIds: [],
});
sessionId: response.sessionId,
role: 'agent' as const,
text: response.messages.map((m) => m.message).join('\n'),
raw: response.messages,
};
await logTurnToHistory(initialEntry, ++this.turnCounter, this.historyDir, this.historyBuffer);

// Write turn-index.json and metadata.json immediately so they exist after session start
await this.historyBuffer.flush();

const agentDir = await getAgentIndexDir(agentIdForStorage);
await logSessionToIndex(agentDir, {
Expand Down
Loading
Loading