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
1 change: 1 addition & 0 deletions packages/agent-toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@mondaydotcomorg/api": "^13.0.0",
"axios": "^1.10.0",
"exceljs": "^4.4.0",
"fast-deep-equal": "^3.1.3",
"jsonwebtoken": "^9.0.2",
"mammoth": "^1.12.0",
"unpdf": "^1.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('GetBoardActivityTool', () => {
});
});

it('includes data field only when includeData is true', async () => {
it('includes data field parsed as an object when includeData is true', async () => {
mocks.setResponse({ boards: [mockBoard] });
const tool = new GetBoardActivityTool(mocks.mockApiClient);

Expand All @@ -49,8 +49,8 @@ describe('GetBoardActivityTool', () => {
board_name: 'Test Board',
board_url: 'https://monday.com/boards/1',
data: [
{ created_at: '2024-01-01T00:00:00Z', event: 'create_pulse', entity: 'pulse', user_id: '123', data: '{"key":"value"}' },
{ created_at: '2024-01-02T00:00:00Z', event: 'update_pulse', entity: 'pulse', user_id: '456', data: '{"foo":"bar"}' },
{ created_at: '2024-01-01T00:00:00Z', event: 'create_pulse', entity: 'pulse', user_id: '123', data: { key: 'value' } },
{ created_at: '2024-01-02T00:00:00Z', event: 'update_pulse', entity: 'pulse', user_id: '456', data: { foo: 'bar' } },
],
});
});
Expand Down Expand Up @@ -122,6 +122,41 @@ describe('GetBoardActivityTool', () => {
);
});

it('trims redundant previous_value from data payloads when includeData is true', async () => {
const files = [
{ assetId: 1, name: 'a.pdf' },
{ assetId: 2, name: 'b.pdf' },
];
const heavyBoard = {
name: 'Heavy Board',
url: 'https://monday.com/boards/9',
activity_logs: [
{
created_at: '2024-03-01T00:00:00Z',
event: 'update_column_value',
entity: 'pulse',
user_id: '77',
data: JSON.stringify({
action_record_uuid: 'uuid-abc',
column_id: 'files',
previous_value: files,
value: files,
}),
},
],
};
mocks.setResponse({ boards: [heavyBoard] });
const tool = new GetBoardActivityTool(mocks.mockApiClient);

const result = await tool.execute({ boardId: 9, includeData: true });
const row = (result.content as { data: Array<{ data: Record<string, unknown> }> }).data[0];

expect(row.data.action_record_uuid).toBe('uuid-abc');
expect(row.data.value).toEqual(files);
expect(row.data.previous_value).toBeUndefined();
expect(row.data.previous_value_omitted).toBe('equals_value');
});

it('propagates API errors', async () => {
mocks.getMockRequest().mockRejectedValueOnce(new Error('Unauthorized'));
const tool = new GetBoardActivityTool(mocks.mockApiClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
GetBoardActivityQueryVariables,
} from '../../../../monday-graphql/generated/graphql/graphql';
import { getBoardActivity } from './get-board-activity.graphql';
import { trimActivityData } from './trim-activity-data';
import { ToolInputType, ToolOutputType, ToolType } from '../../../tool';
import { BaseMondayApiTool, createMondayApiAnnotations } from './../base-monday-api-tool';
import { TIME_IN_MILLISECONDS } from '../../../../utils';
Expand All @@ -28,7 +29,7 @@ export const getBoardActivityToolSchema = {
.optional()
.default(false)
.describe(
'Whether to include the raw data payload for each activity entry. The data field contains the full before/after state of changes and can be very large. Only set to true when you need the detailed change data.',
'Whether to include the parsed data payload for each activity entry. The data field contains the full before/after state of changes and can be very large. Only set to true when you need the detailed change data. When true, `data` is returned as a parsed object (not a JSON string); if `previous_value` equals `value` in a row, `previous_value` is dropped and `previous_value_omitted: "equals_value"` is added in its place.',
),
};

Expand Down Expand Up @@ -102,7 +103,7 @@ export class GetBoardActivityTool extends BaseMondayApiTool<typeof getBoardActiv
event: log.event,
entity: log.entity,
user_id: log.user_id,
...(includeData && log.data ? { data: log.data } : {}),
...(includeData && log.data ? { data: trimActivityData(log.data) } : {}),
})),
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { trimActivityData } from './trim-activity-data';

describe('trimActivityData', () => {
it('returns the parsed object unchanged when data has no previous_value/value pair', () => {
const input = '{"action_record_uuid":"abc-123","key":"value"}';
expect(trimActivityData(input)).toEqual({ action_record_uuid: 'abc-123', key: 'value' });
});

it('returns the original string when JSON parsing fails', () => {
const input = 'not valid json';
expect(trimActivityData(input)).toBe(input);
});

it('returns the raw parsed value when payload is not an object', () => {
expect(trimActivityData('"just a string"')).toBe('just a string');
expect(trimActivityData('[1,2,3]')).toEqual([1, 2, 3]);
expect(trimActivityData('null')).toBeNull();
});

it('drops previous_value when it deep-equals value', () => {
const files = [{ assetId: 1, name: 'a.pdf' }, { assetId: 2, name: 'b.pdf' }];
const input = JSON.stringify({
action_record_uuid: 'uuid-1',
column_id: 'files',
previous_value: files,
value: files,
});
const out = trimActivityData(input) as Record<string, unknown>;
expect(out).toEqual({
action_record_uuid: 'uuid-1',
column_id: 'files',
value: files,
previous_value_omitted: 'equals_value',
});
expect(out.previous_value).toBeUndefined();
});

it('preserves both fields when previous_value differs from value', () => {
const input = JSON.stringify({
previous_value: [1, 2, 3],
value: [1, 2, 3, 4],
});
expect(trimActivityData(input)).toEqual({ previous_value: [1, 2, 3], value: [1, 2, 3, 4] });
});

it('preserves action_record_uuid when trimming', () => {
const files = [{ assetId: 1 }];
const input = JSON.stringify({
action_record_uuid: 'must-survive',
previous_value: files,
value: files,
});
const out = trimActivityData(input) as Record<string, unknown>;
expect(out.action_record_uuid).toBe('must-survive');
expect(out.previous_value_omitted).toBe('equals_value');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import equal from 'fast-deep-equal';

export function trimActivityData(dataJson: string): unknown {
let parsed: unknown;
try {
parsed = JSON.parse(dataJson);
} catch {
return dataJson;
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
return parsed;
}
const obj = parsed as Record<string, unknown>;
if (!('previous_value' in obj) || !('value' in obj)) {
return obj;
}
if (!equal(obj.previous_value, obj.value)) {
return obj;
}
const { previous_value: _drop, ...rest } = obj;
return { ...rest, previous_value_omitted: 'equals_value' };
}
Loading