Merge branch 'staging' of https://github.com/buster-so/buster into staging

This commit is contained in:
Nate Kelley 2025-09-22 13:43:55 -06:00
commit 99d3645054
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 71 additions and 17 deletions

View File

@ -51,9 +51,12 @@ function getLatestVersionNumber(versionHistory: VersionHistory | null): number {
if (!versionHistory || Object.keys(versionHistory).length === 0) {
return 0;
}
// Get all version numbers from the record values
const versionNumbers = Object.values(versionHistory).map((v) => v.version_number);
return Math.max(...versionNumbers);
// Get all version numbers from the record values, filtering out null/undefined/NaN
const versionNumbers = Object.values(versionHistory)
.map((v) => v.version_number)
.filter((n) => typeof n === 'number' && !Number.isNaN(n));
return versionNumbers.length > 0 ? Math.max(...versionNumbers) : 0;
}
// Helper function to add dashboard version to history

View File

@ -296,10 +296,7 @@ export function createModifyReportsDelta(context: ModifyReportsContext, state: M
state.version_number = newVersion;
// Track this modification for this tool invocation
if (!state.reportsModifiedInMessage) {
state.reportsModifiedInMessage = new Set();
}
state.reportsModifiedInMessage.add(state.reportId);
state.reportModifiedInMessage = true;
// Update version history with the final content after all edits
const now = new Date().toISOString();
@ -312,14 +309,39 @@ export function createModifyReportsDelta(context: ModifyReportsContext, state: M
},
};
// Update the database with the result of all edits
// Update the database with the result of all edits (concurrent, not chained)
try {
await batchUpdateReport({
// Initialize pending writes array if needed
if (!state.pendingDbWrites) {
state.pendingDbWrites = [];
}
// We're already inside a check for state.reportId being defined
if (!state.reportId) {
throw new Error('Report ID is unexpectedly undefined');
}
// Create the write promise and add to pending writes
const writePromise = batchUpdateReport({
reportId: state.reportId,
content: currentContent,
name: state.reportName || undefined,
versionHistory,
});
})
.then(() => {
// Convert to void to match the array type
return;
})
.catch((error) => {
console.error('[modify-reports-delta] Database write failed:', error);
throw error;
});
// Add to pending writes array
state.pendingDbWrites.push(writePromise);
// Await this specific write to handle errors
await writePromise;
// No cache update during delta - execute will handle write-through

View File

@ -79,7 +79,8 @@ async function processEditOperations(
edits: Array<{ operation?: 'replace' | 'append'; code_to_replace: string; code: string }>,
messageId?: string,
snapshotContent?: string,
versionHistory?: VersionHistory
versionHistory?: VersionHistory,
state?: ModifyReportsState
): Promise<{
success: boolean;
finalContent?: string;
@ -176,6 +177,27 @@ async function processEditOperations(
// Write all changes to database in one operation
try {
// Wait for ALL pending delta writes to complete before doing final update
if (state?.pendingDbWrites && state.pendingDbWrites.length > 0) {
console.info(
`[modify-reports-execute] Waiting for ${state.pendingDbWrites.length} pending delta writes to complete`
);
try {
// Wait for all writes to complete (some may fail, that's OK)
await Promise.allSettled(state.pendingDbWrites);
// Add small delay to ensure we're absolutely last
await new Promise((resolve) => setTimeout(resolve, 50));
console.info(
'[modify-reports-execute] All delta writes completed, proceeding with final update'
);
} catch (error) {
console.warn(
'[modify-reports-execute] Error waiting for delta writes, proceeding with final update:',
error
);
}
}
await batchUpdateReport({
reportId,
content: currentContent,
@ -239,7 +261,8 @@ const modifyReportsFile = wrapTraced(
params: ModifyReportsInput,
context: ModifyReportsContext,
snapshotContent?: string,
versionHistory?: VersionHistory
versionHistory?: VersionHistory,
state?: ModifyReportsState
): Promise<ModifyReportsOutput> => {
// Get context values
const userId = context.userId;
@ -299,7 +322,8 @@ const modifyReportsFile = wrapTraced(
params.edits,
messageId,
snapshotContent, // Pass immutable snapshot
versionHistory // Pass snapshot version history
versionHistory, // Pass snapshot version history
state // Pass state to access lastDbWritePromise
);
// Track file associations if this is a new version (not part of same turn)
@ -390,7 +414,8 @@ export function createModifyReportsExecute(
input,
context,
state.snapshotContent, // Pass immutable snapshot from state
state.versionHistory // Pass snapshot version history from state
state.versionHistory, // Pass snapshot version history from state
state // Pass state to access lastDbWritePromise
);
if (!result) {

View File

@ -83,7 +83,7 @@ const ModifyReportsStateSchema = z.object({
.string()
.optional()
.describe('Track the last content saved to DB to avoid redundant updates'),
reportsModifiedInMessage: z.set(z.string()).optional(),
reportModifiedInMessage: z.boolean().optional(),
snapshotVersion: z.number().optional(),
versionHistory: z
.record(
@ -101,9 +101,13 @@ const ModifyReportsStateSchema = z.object({
export type ModifyReportsInput = z.infer<typeof ModifyReportsInputSchema>;
export type ModifyReportsOutput = z.infer<typeof ModifyReportsOutputSchema>;
export type ModifyReportsContext = z.infer<typeof ModifyReportsContextSchema>;
export type ModifyReportsState = z.infer<typeof ModifyReportsStateSchema>;
export type ModifyReportsEditState = z.infer<typeof ModifyReportsEditStateSchema>;
// Extend the inferred type to include Promise fields (not supported by Zod directly)
export type ModifyReportsState = z.infer<typeof ModifyReportsStateSchema> & {
pendingDbWrites?: Promise<void>[]; // Track all pending writes
};
// Factory function that accepts agent context and maps to tool context
export function createModifyReportsTool(context: ModifyReportsContext) {
// Initialize state for streaming
@ -117,7 +121,7 @@ export function createModifyReportsTool(context: ModifyReportsContext) {
toolCallId: undefined,
responseMessageCreated: false,
snapshotContent: undefined,
reportsModifiedInMessage: new Set(),
reportModifiedInMessage: false,
};
// Create all functions with the context and state passed