fix(ai) - Fix date stripped from agent tool output (#22443)

https://discord.com/channels/1130383047699738754/1522138489238458569

**Summary**
Fix a bug where Date objects (returned by TypeORM for createdAt,
updatedAt, deletedAt columns) were silently dropped from AI agent tool
responses
stripEmptyValues treated Date instances as empty objects because
Object.entries(new Date()) returns [], causing the function to discard
them
Add instanceof Date guard before the generic object branch so Date
values pass through unchanged
**Root cause**
TypeORM marks createdAt/updatedAt/deletedAt as special columns
(createDate/updateDate/deleteDate) and returns them as JavaScript Date
objects rather than strings. The stripEmptyValues utility checked typeof
value === 'object' (true for Date), then called Object.entries() on it
-- which yields an empty array since Date has no own enumerable
properties -- and concluded the value was "empty".

The existing tests used string dates ('2024-01-01') instead of actual
Date objects, so the bug was never caught.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22443?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Etienne
2026-07-02 12:19:04 +02:00
committed by GitHub
parent 5a4ebca226
commit 080014c970
2 changed files with 48 additions and 0 deletions
@@ -141,6 +141,47 @@ describe('stripEmptyValues', () => {
});
});
it('should preserve Date objects', () => {
const date = new Date('2024-01-15T10:30:00.000Z');
expect(stripEmptyValues({ createdAt: date, name: 'Test' })).toEqual({
createdAt: date,
name: 'Test',
});
});
it('should preserve Date objects inside nested records', () => {
const createdAt = new Date('2024-01-15T10:30:00.000Z');
const updatedAt = new Date('2024-02-20T14:00:00.000Z');
const input = {
result: {
records: [
{
id: 'abc-123',
name: 'Acme',
createdAt,
updatedAt,
deletedAt: null,
},
],
},
};
expect(stripEmptyValues(input)).toEqual({
result: {
records: [
{
id: 'abc-123',
name: 'Acme',
createdAt,
updatedAt,
},
],
},
});
});
it('should handle primitive values', () => {
expect(stripEmptyValues(42)).toBe(42);
expect(stripEmptyValues('hello')).toBe('hello');
@@ -1,11 +1,18 @@
// Recursively strips null, undefined, empty strings, empty objects,
// and empty arrays from a value. Returns undefined if the entire
import { isDate } from '@sniptt/guards';
// value is empty so the caller can decide whether to include it.
export const stripEmptyValues = (value: unknown): unknown => {
if (value === null || value === undefined || value === '') {
return undefined;
}
if (isDate(value)) {
return value;
}
if (Array.isArray(value)) {
const cleaned = value
.map(stripEmptyValues)