@@ -71,6 +71,55 @@ function optionalString(value: unknown): string | undefined {
7171 return trimmed . length > 0 ? trimmed : undefined
7272}
7373
74+ function optionalOpaqueString ( value : unknown ) : string | undefined {
75+ return typeof value === 'string' && value . length > 0 ? value : undefined
76+ }
77+
78+ function optionalBoundedString (
79+ value : unknown ,
80+ label : string ,
81+ maxLength : number
82+ ) : string | undefined {
83+ const normalized = optionalString ( value )
84+ if ( normalized !== undefined && normalized . length > maxLength ) {
85+ throw new Error ( `${ label } must be at most ${ maxLength } characters.` )
86+ }
87+ return normalized
88+ }
89+
90+ function getListPaginationParams ( params : Record < string , unknown > ) {
91+ const pageSize = parseOptionalNumberInput ( params . maxResults , 'Max results' , {
92+ integer : true ,
93+ min : 1 ,
94+ max : 100 ,
95+ } )
96+ const nextLink = optionalOpaqueString ( params . nextLink )
97+ const nextPageSize = parseOptionalNumberInput ( params . nextPageSize , 'Next page size' , {
98+ integer : true ,
99+ min : 1 ,
100+ max : 100 ,
101+ } )
102+
103+ if ( nextLink !== undefined ) {
104+ if ( nextPageSize === undefined ) {
105+ throw new Error ( 'Next page size is required when Next Page URL is provided.' )
106+ }
107+ if ( pageSize !== undefined && pageSize !== nextPageSize ) {
108+ throw new Error ( 'Max results must match Next page size.' )
109+ }
110+ return {
111+ nextLink,
112+ nextPageSize,
113+ ...( pageSize !== undefined && { pageSize } ) ,
114+ }
115+ }
116+
117+ if ( nextPageSize !== undefined ) {
118+ throw new Error ( 'Next page size may only be provided with Next Page URL.' )
119+ }
120+ return { pageSize : pageSize ?? 100 }
121+ }
122+
74123function parseRequiredRecord ( value : unknown ) : Record < string , unknown > {
75124 const parsed = parseOptionalJsonInput < unknown > ( value , 'Record data' )
76125 if ( ! parsed || typeof parsed !== 'object' || Array . isArray ( parsed ) ) {
@@ -344,6 +393,16 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
344393 condition : { field : 'operation' , value : [ 'list_records' , 'list_owners' ] } ,
345394 mode : 'advanced' ,
346395 } ,
396+ {
397+ id : 'nextPageSize' ,
398+ title : 'Next Page Size' ,
399+ type : 'short-input' ,
400+ placeholder : 'Use the nextPageSize output from the previous page' ,
401+ description :
402+ 'Exact continuation page size returned with the previous List Records or List Owners result.' ,
403+ condition : { field : 'operation' , value : [ 'list_records' , 'list_owners' ] } ,
404+ mode : 'advanced' ,
405+ } ,
347406 {
348407 id : 'searchTerm' ,
349408 title : 'Search Term' ,
@@ -544,14 +603,15 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
544603 title : 'Close Subject' ,
545604 type : 'short-input' ,
546605 placeholder : 'Reason or summary for closing the opportunity' ,
606+ description : 'Optional subject for the opportunity-close activity (maximum 200 characters).' ,
547607 condition : { field : 'operation' , value : 'close_opportunity' } ,
548- required : { field : 'operation' , value : 'close_opportunity' } ,
549608 } ,
550609 {
551610 id : 'opportunityDescription' ,
552611 title : 'Close Notes' ,
553612 type : 'long-input' ,
554613 placeholder : 'Optional details about the outcome' ,
614+ description : 'Optional opportunity-close description (maximum 2,000 characters).' ,
555615 condition : { field : 'operation' , value : 'close_opportunity' } ,
556616 mode : 'advanced' ,
557617 } ,
@@ -584,6 +644,7 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
584644 title : 'Resolution Notes' ,
585645 type : 'long-input' ,
586646 placeholder : 'Optional resolution details' ,
647+ description : 'Optional case-resolution description (maximum 100,000 characters).' ,
587648 condition : { field : 'operation' , value : 'close_case' } ,
588649 mode : 'advanced' ,
589650 } ,
@@ -646,12 +707,6 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
646707 switch ( params . operation ) {
647708 case 'list_records' : {
648709 const recordType = getRecordType ( params . recordType )
649- const top =
650- parseOptionalNumberInput ( params . maxResults , 'Max results' , {
651- integer : true ,
652- min : 1 ,
653- max : 100 ,
654- } ) ?? 100
655710 const includeCount = parseBooleanWithDefault (
656711 params . includeCount ,
657712 'Include total count' ,
@@ -672,9 +727,8 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
672727 ...( optionalString ( params . recordExpand ) && {
673728 expand : optionalString ( params . recordExpand ) ,
674729 } ) ,
675- pageSize : top ,
730+ ... getListPaginationParams ( params ) ,
676731 count : includeCount ? 'true' : 'false' ,
677- ...( optionalString ( params . nextLink ) && { nextLink : optionalString ( params . nextLink ) } ) ,
678732 }
679733 }
680734
@@ -744,19 +798,12 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
744798
745799 case 'list_owners' : {
746800 const ownerType = getOwnerType ( params . ownerType )
747- const top =
748- parseOptionalNumberInput ( params . maxResults , 'Max results' , {
749- integer : true ,
750- min : 1 ,
751- max : 100 ,
752- } ) ?? 100
753801 return {
754802 ...common ,
755803 entitySetName : ownerType . entitySetName ,
756804 select : ownerType . select ,
757805 filter : ownerType . filter ,
758- pageSize : top ,
759- ...( optionalString ( params . nextLink ) && { nextLink : optionalString ( params . nextLink ) } ) ,
806+ ...getListPaginationParams ( params ) ,
760807 }
761808 }
762809
@@ -840,14 +887,18 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
840887 'Opportunity status reason' ,
841888 { integer : true , min : - 2_147_483_648 , max : 2_147_483_647 }
842889 )
890+ const subject = optionalBoundedString ( params . opportunitySubject , 'Close subject' , 200 )
891+ const description = optionalBoundedString (
892+ params . opportunityDescription ,
893+ 'Close notes' ,
894+ 2_000
895+ )
843896 return {
844897 ...common ,
845898 opportunityId : requiredString ( params . closeOpportunityId , 'Opportunity ID' ) ,
846899 outcome : requiredString ( params . opportunityOutcome ?? 'won' , 'Outcome' ) ,
847- subject : requiredString ( params . opportunitySubject , 'Close subject' , 200 ) ,
848- ...( optionalString ( params . opportunityDescription ) && {
849- description : optionalString ( params . opportunityDescription ) ,
850- } ) ,
900+ ...( subject && { subject } ) ,
901+ ...( description && { description } ) ,
851902 ...( statusReason !== undefined && { statusReason } ) ,
852903 }
853904 }
@@ -863,13 +914,16 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
863914 'Case status reason' ,
864915 { integer : true , min : - 2_147_483_648 , max : 2_147_483_647 }
865916 )
917+ const description = optionalBoundedString (
918+ params . caseDescription ,
919+ 'Resolution notes' ,
920+ 100_000
921+ )
866922 return {
867923 ...common ,
868924 caseId : requiredString ( params . caseId , 'Case ID' ) ,
869925 subject : requiredString ( params . caseSubject , 'Resolution subject' , 200 ) ,
870- ...( optionalString ( params . caseDescription ) && {
871- description : optionalString ( params . caseDescription ) ,
872- } ) ,
926+ ...( description && { description } ) ,
873927 ...( timeSpent !== undefined && { timeSpent } ) ,
874928 ...( statusReason !== undefined && { statusReason } ) ,
875929 }
@@ -898,6 +952,10 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
898952 maxResults : { type : 'string' , description : 'Maximum results for a single page (1-100)' } ,
899953 includeCount : { type : 'boolean' , description : 'Whether to request the total matching count' } ,
900954 nextLink : { type : 'string' , description : 'Opaque next-page URL from a previous list result' } ,
955+ nextPageSize : {
956+ type : 'string' ,
957+ description : 'Page size paired with the previous list result nextLink' ,
958+ } ,
901959 searchTerm : { type : 'string' , description : 'Dataverse Search query text' } ,
902960 searchSkip : { type : 'string' , description : 'Number of earlier search results to skip' } ,
903961 searchMode : { type : 'string' , description : 'Search mode: any or all' } ,
@@ -973,6 +1031,10 @@ export const MicrosoftDynamics365Block: BlockConfig<DataverseResponse> = {
9731031 description : 'Whether Dataverse capped the provider-reported matching count' ,
9741032 } ,
9751033 nextLink : { type : 'string' , description : 'Opaque provider URL for the next records page' } ,
1034+ nextPageSize : {
1035+ type : 'number' ,
1036+ description : 'Page size that must accompany the next records page URL' ,
1037+ } ,
9761038 results : {
9771039 type : 'json' ,
9781040 description : 'Current page of Dataverse Search results with table-specific attributes' ,
@@ -1117,7 +1179,7 @@ export const MicrosoftDynamics365BlockMeta = {
11171179 description :
11181180 'Close an approved Dynamics 365 opportunity as won or lost with an explicit outcome summary.' ,
11191181 content :
1120- '# Close Sales Opportunity\n\nUse this skill only after the user or an authorized workflow step has approved the final outcome.\n\n## Steps\n1. Retrieve the opportunity and confirm its record ID and intended outcome.\n2. Ask for a close subject and optional notes.\n3. Use Close Opportunity with won or lost; supply a custom status-reason integer only when the environment defines it.\n4. Do not retry the close action automatically.\n5. Report the supplied opportunity ID and outcome without claiming a response status Dynamics 365 did not return.\n\n## Output\nThe closed opportunity ID, chosen outcome, and action success.' ,
1182+ '# Close Sales Opportunity\n\nUse this skill only after the user or an authorized workflow step has approved the final outcome.\n\n## Steps\n1. Retrieve the opportunity and confirm its record ID and intended outcome.\n2. Optionally gather a close subject and notes.\n3. Use Close Opportunity with won or lost; supply a custom status-reason integer only when the environment defines it.\n4. Do not retry the close action automatically.\n5. Report the supplied opportunity ID and outcome without claiming a response status Dynamics 365 did not return.\n\n## Output\nThe closed opportunity ID, chosen outcome, and action success.' ,
11211183 } ,
11221184 {
11231185 name : 'resolve-customer-case' ,
0 commit comments