Skip to content
Open
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
25 changes: 18 additions & 7 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
label: string; // for debug logging
}

class ApiTransactionManager {

Check warning on line 70 in src/background.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark these members as `readonly`.

See more on https://sonarcloud.io/project/issues?id=shouri123_Late-Meet&issues=AZ9_OAtbZjGpdJcGYNIW&open=AZ9_OAtbZjGpdJcGYNIW&pullRequest=880
private static instance: ApiTransactionManager | null = null;
private static readonly MAX_RETRIES = 5;
private static readonly BASE_DELAY_MS = 1_000;
Expand Down Expand Up @@ -1059,8 +1059,13 @@
async function summarizeTranscriptIfNeeded() {
if (!state.isActive || state.transcript.length === 0) return;

// Bail out immediately if another summarization is already running.
// Claim the in-flight slot synchronously *before* any `await`. The previous
// guard checked `summaryInFlight` here but only set it after several awaits,
// leaving a TOCTOU window where two concurrent triggers (e.g. a timer tick
// and a manual action) could both pass the guard and run in parallel, doubling
// API cost and racing the state writes (#739). Setting it now closes that gap.
if (summaryInFlight) return;
summaryInFlight = true;

const settings = await getSettings();
const requestedInterval = Number(settings.summarizationInterval);
Expand All @@ -1071,10 +1076,16 @@
if (intervalSeconds > 900) intervalSeconds = 900;
const lastSum = state.lastSummarizedAt || 0;
const elapsed = Math.floor((Date.now() - lastSum) / 1000);
if (lastSum > 0 && elapsed < intervalSeconds) return;
if (lastSum > 0 && elapsed < intervalSeconds) {
summaryInFlight = false;
return;
}

const apiKey = await getApiKey();
if (!apiKey) return;
if (!apiKey) {
summaryInFlight = false;
return;
}

const transcriptWindow = state.transcript
.slice(-TRANSCRIPT_WINDOW_SIZE)
Expand All @@ -1084,10 +1095,10 @@
return `[${chunkId}] [${timestampLabel}] ${sanitizePromptText(e.speaker)}: ${sanitizePromptText(e.text)}`;
})
.join("\n");
if (!transcriptWindow.trim()) return;

// Claim the in-flight slot *after* all cheap pre-checks pass.
summaryInFlight = true;
if (!transcriptWindow.trim()) {
summaryInFlight = false;
return;
}

try {
const topicDetectionEnabled = isFeatureEnabled(settings, "topicDetection");
Expand Down