Skip to content
Merged
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
46 changes: 46 additions & 0 deletions packages/core/src/utils/openaiLogger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,52 @@ describe('OpenAILogger', () => {
);
});

it('should include a subagent suffix without the session id', async () => {
const logger = new OpenAILogger(testTempDir);
await logger.initialize();

const request = {
model: 'claude-opus-4-7',
messages: [{ role: 'user', content: 'test' }],
};
const response = { id: 'test-id', choices: [] };

const logPath = await logger.logInteraction(
request,
response,
undefined,
'e097d32b-82d6-422a-afa6-f6184565a8ab#Explore-g2tss0#7',
);

const basename = path.basename(logPath);
expect(basename).toMatch(
/openai-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.\d{3}Z-[a-f0-9]{8}-subagent-Explore-g2tss0\.json/,
);
expect(basename).not.toContain('e097d32b');
});

it('should not include a suffix for main-session prompt ids', async () => {
const logger = new OpenAILogger(testTempDir);
await logger.initialize();

const request = {
model: 'claude-opus-4-7',
messages: [{ role: 'user', content: 'test' }],
};
const response = { id: 'test-id', choices: [] };

const logPath = await logger.logInteraction(
request,
response,
undefined,
'e097d32b-82d6-422a-afa6-f6184565a8ab########0',
);

expect(path.basename(logPath)).toMatch(
/openai-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.\d{3}Z-[a-f0-9]{8}\.json/,
);
});

it('should write correct log data structure', async () => {
const logger = new OpenAILogger(testTempDir);
await logger.initialize();
Expand Down
36 changes: 29 additions & 7 deletions packages/core/src/utils/openaiLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,38 @@

const debugLogger = createDebugLogger('OPENAI_LOGGER');

function sanitizePromptIdForFilename(
promptId: string | undefined,
function sanitizeDiagnosticSuffix(
suffix: string | undefined,
): string | undefined {
if (!promptId || !isInternalPromptId(promptId)) return undefined;
const sanitized = promptId
if (!suffix) return undefined;
const sanitized = suffix
.replace(/[^a-zA-Z0-9._-]+/g, '-')
.replace(/^-+|-+$/g, '');

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
return sanitized || undefined;
}

function extractSubagentSuffix(promptId: string): string | undefined {
const parts = promptId.split('#');
if (parts.length !== 3) return undefined;

const [, subagentId, turn] = parts;
if (!subagentId || !turn || !/^\d+$/.test(turn)) {
return undefined;
}

return `subagent-${subagentId}`;
}

function promptIdSuffixForFilename(
promptId: string | undefined,
): string | undefined {
if (!promptId) return undefined;
if (isInternalPromptId(promptId)) {
return sanitizeDiagnosticSuffix(promptId);
}
return sanitizeDiagnosticSuffix(extractSubagentSuffix(promptId));
}

/**
* Logger specifically for OpenAI API requests and responses
*/
Expand Down Expand Up @@ -75,8 +97,8 @@
* @param request The request sent to OpenAI
* @param response The response received from OpenAI
* @param error Optional error if the request failed
* @param promptId Optional prompt id; internal prompt ids are appended to
* the filename after timestamp and id.
* @param promptId Optional prompt id; internal and subagent prompt ids are
* appended to the filename after timestamp and id.
* @returns The file path where the log was written
*/
async logInteraction(
Expand All @@ -91,7 +113,7 @@

const timestamp = new Date().toISOString().replace(/:/g, '-');
const id = uuidv4().slice(0, 8);
const promptIdSuffix = sanitizePromptIdForFilename(promptId);
const promptIdSuffix = promptIdSuffixForFilename(promptId);
const filename = promptIdSuffix
? `openai-${timestamp}-${id}-${promptIdSuffix}.json`
: `openai-${timestamp}-${id}.json`;
Expand Down
Loading